はじめに
本投稿はJava言語で学ぶデザインパターン入門のデザインパターンをまとめた記事です。今回はPrototypeパターンになります。
まとめ一覧はこちら
Prototypeパターン
Prototypeとは
Prototypeとはインスタンスを生成する時のデザインパターンで、クラスからインスタンスを生成するのではなく、インスタンスから別のインスタンスを(コピーして)生成するパターンのことです。
Prototypeと言う英単語の意味
Weblio辞書参照
原型、模範、原形
Prototypeの意味を見てわかるように単にインスタンスを生成するのではなく、原型や元となるインスタンスを元に新しいインスタンスを生成します。
Prototypeパターンのクラス図
- Prototype: インスタンスを複製して、新しいインスタンスを生成するメソッドを定める
- ConcretePrototype: 実際にインスタンスを複製して、新しいインスタンスを生成するメソッドを実装
- Client: 実際にインスタンスを複製して新しいインスタンスを生成する役割を担う
具体例
本書に記載されている例を参考に、与えた文字に対して下線を引いて表示するクラスと枠線で囲って表示するクラスを用いて紹介していきます
- Prototype: Productインターフェイス
- ConcretePrototype:
- MessageBoxクラス(枠線で囲って表示)
- UnderLinePenクラス(下線表示)
- Client: Managerクラス
Mainクラス
use()
メソッドに渡した文字をクラスの生成時に渡した記号で、装飾しています
public static void main(String[] args) {
Manager manager = new Manager();
UnderLinepen upen = new UnderLinePen('-');
MessageBox mbox = new MessageBox('@');
MessageBox sbox = new MessageBox('♪');
manager.register("stong message", upen);
manager.register("warning box", mbox);
manager.register("slash box", sbox);
Product p1 = manager.create("stong message");
p1.use("Git Hub");
Product p2 = manager.create("warning box");
p2.use("Qiita");
Product p3 = manager.create("slash box");
p3.use("Java");
}
Productインターフェイス
複製のためのメソッドを定めるインタフェース
public interface Product extends Cloneable {
public abstract void use(String s);
public abstract Product createClone();
}
-
createClone()
実際にインスタンスを複製する -
use()
に文字列を渡すとその文字列を装飾して表示する
java.lang.Cloneableを継承していることで、Clone()
メソッドでインスタンスを複製できます
Managerクラス
Productインタフェースを利用してインスタンスの複製を行う
public class Manager {
private HashMap showcase = new HashMap();
public void register(String name, Product prototype) {
showcase.put(name, prototype);
}
public Product create(String protoname) {
//※
Product p = (Product)showcase.get(protoname);
return p.createClone();
}
}
- 複製元のインスタンスを保持しとくためのshowcaseを定義。
※Productのインスタンスをクローンすることで、クラス名に依存することなくProductインターフェイスを実装したクラスを利用することができる。
MessageBoxクラス
- 文字列の周りを枠線で囲む
- Productインターフェースを実装
public class MessageBox implements Product {
private char decoChar;
public MessageBox(char decoChar) {
this.decoChar = decoChar;
}
@Override
public void use(String string) {
int length = string.getBytes().length;
for (int i = 0; i < length + 4; i++) {
System.out.print(decoChar);
}
System.out.println("");
System.out.println(decoChar + " " + string + " " + decoChar);
for (int i = 0; i < length + 4; i++) {
System.out.print(decoChar);
}
System.out.println("");
}
//Productインターフェイスを実装してなければCloneNotSupportedExceptionが投げられる
@Override
public Product createClone() {
Product p = null;
try {
p = (Product)clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
UnderLinePenクラス
- 文字列に下線を表示
- Productインターフェースを実装
public class UnderLinePen implements Product {
private char ulChar;
public UnderLinePen(char ulChar) {
this.ulChar = ulChar;
}
@Override
public void use(String string) {
int length = string.getBytes().length;
System.out.println(string);
System.out.print("");
for (int i = 0; i < length; i++) {
System.out.print(ulChar);
}
System.out.println("");
}
@Override
public Product createClone() {
Product p = null;
try {
p = (Product)clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
実行結果
Git Hub
-------
@@@@@@@@@
@ Qiita @
@@@@@@@@@
♪♪♪♪♪♪♪♪
♪ Java ♪
♪♪♪♪♪♪♪♪
追記
本書では以下のような場合にPrototypeパターンを利用すべきだと記述してあります
- 種類が多すぎてクラスにまとめられない場合
- クラスからのインスタンス生成が難しい場合
- フレームワークと生成するインスタンスを分けたい場合
例えば、図形を描くエディターのようなアプリケーションでは、図形をコピーして描画する機能があると思います。図形などをコピーする際は元の図形を「プロトタイプ」として登録し、コピーしたほうが簡単にできます。
- 状態を持つインスタンスをコピーするする場合のみ有用
無名クラスだとそもそも状態を持たないので複製する必要がない。無名クラスの場合はファクトリメソッドを使うべき
サンプルコードについて
以下のレポジトリにソースコードをアップしてあります。
shoheiyokoyama/design-pattern_java
デザインパターン
- 振る舞いに関するパターン
- Iterator
- Template Method
- Observer
- 構造に関するパターン
- Adapter
- 生成に関するパターン
- Factory Method
- Singleton