2010年5月16日日曜日

Prototypeパターン

Prototypeパターンとは、インスタンスをnewで生成するのではなく、既にあるインスタンスからコピーをして新しいインスタンスを作るパターンです。このパターンを使う場面としては、似たようなクラスを大量に作りたくない場合や、インスタンス生成が難しい場合(*1)、F/W(*2)とインスタンス生成を分けたい場合などです。

コード例としては、複数のパーツを用意してそれを利用するという形にしています。パーツをコピーできるようにするためのPartインターフェースと、それを利用するManagerクラスをF/Wで用意します。パーツの雛形(まさしく、プロトタイプ)はPartインターフェースを実装すればよいです。実際にサンプルコードを動かしたいときは、PartインターフェースとManagerクラスのソースをframeworkフォルダに入れ、それ以外はframeworkフォルダの外においてください。

Partインターフェースは単純に以下のような感じです。Partはコピーできるので、copy()を用意しています。copy()を実際に実装するときはjava.lang.Objectのclone()を呼ぶので、cloneableインターフェースを継承しています。

  1. package framework;  
  2. import java.lang.Cloneable;  
  3.   
  4. public interface Part extends Cloneable {  
  5.     void doSomething();  
  6.     Part copy();  
  7. }  

ManagerクラスはPartインターフェースを介してインスタンスのコピーを行うクラスです。register()はPartの登録、copyPart()はPartをコピーして、それを取得します。

  1. package framework;  
  2. import java.util.Map;  
  3. import java.util.HashMap;  
  4.   
  5. public class Manager {  
  6.     private Map<String, Part> showcase = new HashMap<String, Part>();  
  7.     public void register(String name, Part part) {  
  8.         showcase.put(name, part);  
  9.     }  
  10.     public Part copyPart(String name) {  
  11.         Part p = (Part)showcase.get(name);  
  12.         return p.copy();  
  13.     }  
  14. }  

Partインターフェースを実装するクラスでは、doSomething()はそのパーツにあった処理(パーツの名前を決めたり、読み込むファイルを指定したり、フラグを立てたり・・・なんでも構わない)を定義すればよく、copy()はただ単純にjava.lang.Objectのclone()を呼べばいいです。(*3) 

Main(PartとManagerを利用するクラス)では以下のような感じです。最初はもちろんPartインスタンスを生成して登録しておく必要がありますが、その後はManagerさんにcopyPart()をお願いして、コピーを頂くだけです。
  1. import framework.Part;  
  2. import framework.Manager;  
  3.   
  4. public class Main {  
  5.     public static void main(String[] args) {  
  6.         // 準備  
  7.         Manager manager = new Manager();  
  8.         SamplePart part1 = new SamplePart("part1");  
  9.         SamplePart2 part2 = new SamplePart2("part2");  
  10.         manager.register("abc", part1);  
  11.         manager.register("def", part2);  
  12.   
  13.         // 生成  
  14.         Part p1 = manager.copyPart("abc");  
  15.         p1.doSomething();  
  16.         Part p2 = manager.copyPart("def");  
  17.         p2.doSomething();  
  18.     }  
  19. }  


register()に入れる名前は分かりやすいものがいいでしょう。コピーをお願いするときはその名前を指定します。例が今ひとつ分かりづらいですが、雛形としてのPartはManagerに登録されているので、わざわざパーツを一つずつ自分で作って(コーディングして)、そのクラスをnewする必要がなくなります。単純に言えば再利用性が高いということです。

また、ManagerクラスはPartを扱うクラスではあるものの、実際のPart(例で言うと、SamplePartやSamplePart2)については知らなくて構わないので、パーツとマネージャの依存関係が低いと言えます。

関連パターン
「Flyweightパターン」(勉強中)
「Mementoパターン」(勉強中)
Compositeパターン」Compositeで複雑な構造のインスタンスを作る場合にはprototypeを利用すると良いかもしれない(と本には書かれている)
Decoratorパターン」Decoratorで複雑な構造のインスタンスを作る場合にはprototypeを利用すると良いかもしれない(と本には書かれている)
「Commandパターン」(勉強中)

*1 例としては、グラフィックエディタなどでユーザがマウス操作によって作り上げた図形を再び作りたい場合。プログラミングでこれを作るのは大変なので、ユーザ操作によって作られた図形インスタンスをあらかじめ保存しておいて、それを作りたいときにコピーする(と本には書かれている)
*2 F/W frameworkの略称
*3 本当はclone()の実装についていろいろ説明したいが、本筋から離れるので割愛。気になる人は「Effective Java」のp.54にある「項目11 cloneを注意してオーバーライドする」を参照されたし

0 件のコメント:

コメントを投稿