はじめに
だんだん, タイトルが毎回同じになってきた.
今回はプロトタイプパターンの実装を Java で実施してみた.
Prototype パターン
生成するオブジェクトの原型をコピーして新しいオブジェクトを生成する.
Abstract Factory と似ている.
- new でオブジェクトを生成すれば Abstract Factory./ Factory Method.
- clone をつかう場合の Prototype.
複製を作成するためのメソッドを用意する. といういたって単純なもの.
プロトタイプ が複製を担当し, それ以外の生成における操作をクライアントが 担っている.
Map にテンプレートを登録しておいて, 利用するときに複製する. バイナリデータをマップにいれておいて, キーとなる名前をつけて管理する,など.
(実際に仕事では, バイナリのパケットをテンプレートから生成する処理につかった)
メリット
インスタンスのコンストラクタ引数で差分を渡すことで, クラスの数をかなり減らすことができる.
Java には, Clonable インタフェースがある.
利用シーン
- Abstract Factory パターンでなされるように,
クライアント・アプリケーションにおいて オブジェクトの生成者をサブクラスにすることを回避する
- 標準的な方法 (例えば’new') で新しいオブジェクトを作ることによる
固有のコストが所与のアプリケーションにとって高すぎる時にそれを回避する.
サンプルコード
import java.util.HashMap;
import java.util.Map;
public class PrototypeSample {
public static void main (String args[]) {
printerFacotry factory = new printerFacotry ();
Printer printer;
printer = factory.create ("type a");
printer.printMessage ();
printer = factory.create ("type b");
printer.printMessage ();
printer = factory.create ("type c");
printer.printMessage ();
}
}
class Printer implements Cloneable {
String str;
public Printer (String str) {
this.str = str;
}
public void printMessage () {
System.out.println (str);
}
@Override
public Printer clone () {
Printer cloned = null;
try {
cloned = (Printer) super.clone ();
} catch (CloneNotSupportedException e) {
e.printStackTrace ();
}
return cloned;
}
}
class printerFacotry {
Map<String, Printer> protoMap;
public printerFacotry (){
protoMap = new HashMap<String, Printer>();
protoMap.put ("type a", new Printer ("a"));
protoMap.put ("type b", new Printer ("b"));
protoMap.put ("type c", new Printer ("c"));
}
public Printer create (String type) {
return protoMap.get (type).clone ();
}
}
状態をもたないならば, プロトタイプは不要
無名クラスをクローンできるのかと思ったが, できなかった. そもそも, 無名クラスは状態を持たないので, クローンする必要がなかった.
この比較から以下のことが分かる.
- 状態をもつオブジェクトをコピーするのはプロトタイプパターンが有用.
- 状態をもたないオブジェクトは new で生成する ファクトリメソッドパターンが有用.
クロージャをわたす
Map のなかにクロージャを入れて, 好きな時に取り出すようにした.
これはけっこういいパターンかもしれない. 個人的に気に入った.
import java.util.HashMap;
import java.util.Map;
public class PrototypeSample {
public static void main (String args[]) {
printerFacotry factory = new printerFacotry ();
Printer printer;
printer = factory.create ("type a");
printer.printMessage ();
printer = factory.create ("type b");
printer.printMessage ();
printer = factory.create ("type c");
printer.printMessage ();
}
}
interface Printer {
public void printMessage ();
}
class printerFacotry {
Map<String, Printer> protoMap;
public printerFacotry (){
protoMap = new HashMap<String, Printer>();
protoMap.put ("type a", new Printer (){
public void printMessage () { System.out.println ("a"); }
});
protoMap.put ("type b", new Printer (){
public void printMessage () { System.out.println ("b"); }
});
protoMap.put ("type c", new Printer (){
public void printMessage () { System.out.println ("c"); }
});
}
public Printer create (String type) {
return protoMap.get (type);
}
}
クロージャ + 引数
さらに改良.
クロージャを Map に保存しておいて, 呼び出し時に外部から引数を与えるようにした.
これで, さらにメソッドが柔軟になった.
interface Printer {
public void printMessage (String str);
}
class printerFacotry {
Map<String, Printer> protoMap;
public printerFacotry (){
protoMap = new HashMap<String, Printer>();
protoMap.put ("type a", new Printer (){
public void printMessage (String str) { System.out.println ("**"+str+"**"); }
});
protoMap.put ("type b", new Printer (){
public void printMessage (String str) { System.out.println ("++"+str+"++"); }
});
protoMap.put ("type c", new Printer (){
public void printMessage (String str) { System.out.println ("=="+str+"=="); }
});
}
public Printer create (String type) {
return protoMap.get (type);
}
}
もはや, プロトタイプパターンの記事ではなくなっているが….