デザインパターンを覚えたい (Facade)
facadeパターン
複雑な処理の窓口となるクラスを用意し、複雑なAPIの操作を隠蔽するパターン。ポイントは、
といったところ?これだけだとあまりにも普通なので他にもっとメリットあるのかも。
実装。前回同様Java強化中なのでJavaで。巡回セールスマンを解く窓口を作ってみる。
http://dl.dropbox.com/u/7810000/code/design_pattern/facade.zip
実行。
$ ant test
Buildfile: /Users/xxx/tmp/facade/build.xmlbuild:
[javac] Compiling 1 source file to /Users/xxx/tmp/facade/classestest:
[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近傍を用いた局所探索で巡回セールスマン問題を解いて、結果を画像で出力している。
ちなみに元データでの巡回路がこれ。
最適化後の巡回路がこれ。
結果は一目瞭然ですね。ちなみに、やってる探索は非常に単純なものなので、本当はもっと短い経路があると思われる。
中でやってる処理は大きく分けると、
- マップデータ読み込み
- 探索前のデータを画像出力
- 探索
- 探索後のデータを画像出力
という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]); } }