デザインパターンを覚えたい (Facade)

facadeパターン

複雑な処理の窓口となるクラスを用意し、複雑なAPIの操作を隠蔽するパターン。ポイントは、

  • 相互に関係しあるAPIの処理フローをひとつのメソッドにまとめることにより、使う側から見てわかりやすいAPIにする

といったところ?これだけだとあまりにも普通なので他にもっとメリットあるのかも。

実装。前回同様Java強化中なのでJavaで。巡回セールスマンを解く窓口を作ってみる。
http://dl.dropbox.com/u/7810000/code/design_pattern/facade.zip

実行。

$ ant test
Buildfile: /Users/xxx/tmp/facade/build.xml

build:
[javac] Compiling 1 source file to /Users/xxx/tmp/facade/classes

test:
[java] SalesmanFacade: load mapFile[resources/map.dat]
[java] SalesmanFacade: create mapImage[images/orgMap.jpeg]
[java] SalesmanFacade: resolve the problem
[java] before distance = 16543.048701547556
[java] after distance = 2674.8979194573894
[java] SalesmanFacade: create mapImage[images/resolvedMap.jpeg]

BUILD SUCCESSFUL
Total time: 1 second

今回のは2-Opt近傍を用いた局所探索で巡回セールスマン問題を解いて、結果を画像で出力している。
ちなみに元データでの巡回路がこれ。

最適化後の巡回路がこれ。

結果は一目瞭然ですね。ちなみに、やってる探索は非常に単純なものなので、本当はもっと短い経路があると思われる。

中でやってる処理は大きく分けると、

  1. マップデータ読み込み
  2. 探索前のデータを画像出力
  3. 探索
  4. 探索後のデータを画像出力

という4つの処理を行なっているけど、これをSalesmanFacadeの中で一つのメソッドにまとめている。

public class SalesmanFacade {
    public static final String ORG_MAP_IMAGE = "images/orgMap.jpeg";
    public static final String RESOLVED_MAP_IMAGE = "images/resolvedMap.jpeg";

    public static void resolveTravelingSalesman(String mapFile) {
        try {
            System.out.println("SalesmanFacade: load mapFile[" + mapFile + "]");
            List<Town> townList = MapLoader.loadMap(mapFile);

            System.out.println("SalesmanFacade: create mapImage[" + ORG_MAP_IMAGE + "]");
            MapImage.saveMapImage(townList, ORG_MAP_IMAGE);

            System.out.println("SalesmanFacade: resolve the problem");
            TwoOptSolver solver = new TwoOptSolver(townList);
            double orgDist = solver.calcTotalDistance();
            System.out.println("  before distance = " + Double.toString(orgDist));
            solver.resolve();
            double resolvedDist = solver.calcTotalDistance();
            System.out.println("  after distance = " + Double.toString(resolvedDist));

            System.out.println("SalesmanFacade: create mapImage[" + RESOLVED_MAP_IMAGE + "]");
            MapImage.saveMapImage(solver.getTownList(), RESOLVED_MAP_IMAGE);
        } catch (Exception e) {
            System.out.println("ERROR: failed to resolve the problem: Exception = " + e);
        }
    }
}

SalesmanFacadeで必要な処理を全部まとめているため、Mainではこの処理を呼ぶだけでいい。なので、Mainがとってもシンプルになっている。

public class Main {
    public static void main(String[] args) {
        if (args.length < 1) {
            System.out.println("ERROR: please specify mapFile");
            return;
        }

        // Facadeのメソッドを呼ぶ
        SalesmanFacade.resolveTravelingSalesman(args[0]);
    }
}