導入
同じことをするにもいろんな方法があるものです。トンカツの添え物キャベツは千切りがよい人、
さて、
展開
Strategy(ストラテジ)
プログラムは何らかの目的があって作られます。目的を果たすためにアルゴリズムをコードに落とします。そのアルゴリズムは1つとは限りません。アルゴリズムによって処理速度の違いや効率の善し悪しがあります。あるデータに対して高速だったアルゴリズムが、
Strategyパターンの目的
Strategyという単語は、
目的のために使用できるアルゴリズムが複数あるとします。場合に応じてアルゴリズムを切り替えて使用できれば便利です。アルゴリズムを部品化して、
また、
[Strategyパターン]
主となるコードからアルゴリズムを表現するコードを切り離し、
![画像](/assets/images/dev/serial/01/practical-programming-with-processing/0025/thumb/TH800_strategy.jpg)
上図のStrategyパターンでは、
アルゴリズムを実装したConcreteStrategyクラスは複数あります。ConcreteStrategyクラスはStrategyインタフェイスを実装しています。
ContextクラスはStrategyインタフェイスを通してConcreteStrategyクラスのメソッドstrategyMethodを使用します。Contextクラスのコードでは、
今後、
Strategyパターンを使わないと
同じ目的のために作られた、
MultiAlgorithm.pde
)void setup(){
int n = 10;
println("Sum of 1 to " + n + " is ...");
println("Switch algorithm.");
for ( int i = 0; i < n; i++ ){
int result = (i%2 == 0) ? sumSlow(n)
: sumFast(n);
println(i + " : sum(" + n + ") = " + result);
}
}
int sumSlow(int n){
println("<sumSlow>");
int val = 0;
for(int i=0; i <= n; i++){
val = val + i;
}
return val;
}
int sumFast(int n){
println("<sumFast>");
int val = n * (n+1) / 2;
return val;
}
アルゴリズムをクラスでカプセル化する
アルゴリズムを実装したメソッドを、
アルゴリズムをカプセル化したSlowSum
とFastSum
クラスのコードは紙面の都合上省略しますので、
NoStrategyPattern.pde
を参照)void setup(){
int n = 10;
println("Sum of 1 to " + n + " is ...");
println("Switch algorithm.");
SlowSum ss = new SlowSum();
FastSum fs = new FastSum();
for ( int i = 0; i < n; i++ ){
int result = (i%2 == 0) ? ss.sum(n)
: fs.sum(n);
println(i + " : sum(" + n + ") = " + result);
}
}
Stragegyパターンを使う
それでは、
WithStrategyPattern.pde
の一部)void setup(){
int n = 10;
println("Sum of 1 to " + n + " is ...");
println("Switch algorithm.");
Strategy st;
for ( int i = 0; i < n; i++ ){
st = (i%2 == 0) ? new ConcreteStrategy1()
: new ConcreteStrategy2();
Context c = new Context(st);
int result = c.getSum(n);
println(i + " : sum(" + n + ") = " + result);
}
}
sketchの全体を以下に示します。
このsketchでは、Context
クラスは、
Context
クラスのコードContext.pde
)class Context{
Strategy currentStrategy;
Context(Strategy s){
currentStrategy = s;
}
public int getSum(int n){
return currentStrategy.sum(n);
}
}
Context
クラスから具体的なアルゴリズムを記述したコードが完全に切り離されています。具体的なアルゴリズムを記述したコードを持つオブジェクトへの参照は、Strategy
インタフェイスのオブジェクトcurrentStrategy
が持つだけです。ConcreteStrategyにあたるクラスがどんな実装を持つかは、Context
クラスには関係なくなりました。主となるsketchでは次のように用いられています。
WithStrategyPattern.pde
の一部 st = (i%2 == 0) ? new ConcreteStrategy1()
: new ConcreteStrategy2();
Context c = new Context(st);
int result = c.getSum(n);
Context
クラスは生成時にコンストラクタへアルゴリズムを担当するオブジェクトへの参照を受け取ります。その後は、getSum
を呼べば目的の仕事が果たせます。
ここでは、
- 「合計を求めるクラス」
を切り替えて使うのか - 合計を求めるアルゴリズムを実装したメソッドを持つクラスを切り替えて、
「合計を求めるクラス」 はそのまま使うのか
しかしていません。そのため、
Strategyパターンを使用するメリットを考えるため、
事例:Robocodeでロボットの動作アルゴリズムを動的に切り替える
これは実際に私がRobocodeに取り組む中で体験したことです。
移動する相手ロボットに、私のロボットが発射した弾を当てるためには、相手ロボットの動きに応じた
- 直線的に動く相手なのか、
曲線的に動く相手なのか。 - 直線的に動くとしても、
前後に往復するのか、 何かのタイミングで方向転換を行うのか。 - 移動速度の変化にパターンはないか。
これらは単純に1つ、
主となるコードに複数のアルゴリズムを記述したコードが含まれると、
Strategyパターンを適用せずに、
演習
演習1(難易度:middle)
ディスプレイウインドウにアルゴリズム切り替えのための白いボタンを表示してください。そして、
まとめ
- Strategyパターンを使うメリットを学びました。
- Strategyパターンの使い方を学びました。
学習の確認
それぞれの項目で、
- Strategyパターンを使うメリットがわかりましたか?
- メリットが分かった。自分のプログラミングにも使いたい。
- 本文に解説されたメリットを理解することはできたが、
それほどメリットだとは感じない。 - 本文が理解できない。
- Strategyパターンを使えるようになりましたか?
- 使えるようになった。自分のプログラミングにも活用できそうだ。
- 本文の例を理解することはできたが、
自分のプログラミングに活用できる気がしない。 - 本文の例が理解できない。
参考文献
- 『増補改訂版Java言語で学ぶデザインパターン入門』
(結城浩 著、 ソフトバンククリエイティブ) - 誰もが認める最も分かりやすいデザインパターン入門書。
- 『オブジェクト指向における再利用のためのデザインパターン』
(Eric Gamma 著、 ソフトバンククリエイティブ) - デザインパターンの原典。別称
『GoF本』。
- デザインパターンの原典。別称
- 『Java デザインパターン徹底攻略』
(日立ソフトウェアエンジニアリング (株) インターネットビジネス部 著、 技術評論社) - 前掲のGoF本の解説書で、
サンプルはJava言語。絶版。強く再版を望みます。
- 前掲のGoF本の解説書で、
- 『Robocodeで学ぶ 一歩先のJavaプログラミング』
(平田敦 著、 カットシステム) - Robocodeを使ったJava言語の学習書です。敵ロボットに照準を合わせるためのアルゴリズムにStrategyパターンを適用した解説をしています。参考にしてみてください。
演習解答
- Strategyパターンを適用したsketchを以下に示します。これらのpdeファイルをすべて
GCD_
フォルダに納めてください。実行時にディスプレイウインドウ上の白いボタンをクリックすると、Strategy アルゴリズムが切り替わります。座標値の最大公約数や使用したアルゴリズムがコンソールに刻々と表示される様子を確認してください。 - GCD_
Strategy. :主となるsketch。Clientクラスに対応。pde - Strategy.
pde :Strategyインタフェイスに対応。 - EuclideanAlgorithmStandard.
pde :ConcreteStrategyクラスに対応。 - EuclideanAlgorithmRecursive.
pde :ConcreteStrategyクラスに対応。 - Calculator.
pde :Contextクラスに対応。
- GCD_