blog

デザイン・パターン プロトタイプ・パターン

もう1つの比較的単純なパターンであるプロトタイプパターンは、オブジェクトの作成パターンです。その主な機能は、プロトタイプオブジェクトから複数の同じオブジェクトをクローンすることです。 私の個人的な意見...

Dec 15, 2020 · 5 min. read
シェア

もうひとつの比較的単純なパターンであるプロトタイプ・パターンは、オブジェクト作成パターンに属します。主な役割は、プロトタイプ・オブジェクトを通して複数の同じオブジェクトをクローンすることです。

私の個人的な意見では、デザインパターンは2つのカテゴリに分けることができます:1つは、システムのデカップリング、システムのスケーラブル化など、コードの構造を最適化するために使用されます。もう1つのタイプのパターンは、システムの構造を助けるものではなく、構造的なものではなく、機能的なものです。

パターンの背景

UML

原則

プロトタイプクラスはクローンメソッド自体を提供させ、ユーザーは直接プロトタイプクラスのオブジェクトのクローンメソッドを呼び出すと、同じプロトタイプオブジェクトのクローンをクローンすることができます。

クローン化されたオブジェクトは完全に新しいオブジェクトであり、新しく作成されたメモリ空間であって、そのようなオブジェクトへの参照ではないことに注意してください。

ものだね

  • プロトタイプパターンを表すインターフェース。
  • インターフェースを実装したプロトタイプクラス。
  • クローンメソッドを提供します。

実装

言語の性質によって、さまざまな実装があります。ここでは、Javaの実装と一般的な実装についてのみ説明します。

一般的な実装

一般的な実装は、自分で新しいオブジェクトを作成し、プロトタイプ・オブジェクトのプロパティをこの新しいオブジェクトに再割り当てすることです。この方法は、すべてのオブジェクト指向言語に共通です。

プロトタイプ・パターンを示すプロトタイプ・インターフェースを定義します:

public interface Prototype {
 Prototype clone();
}

プロトタイプ・クラス 汎用実装

public class BasePrototypeDemo implements Prototype {
 private String attr;
 public String getAttr() {
 return attr;
 }
 public void setAttr(String attr) {
 this.attr = attr;
 }
 //内部で新しいオブジェクトをクローンする
 @Override
 public Prototype clone() {
 BasePrototypeDemo instance = new BasePrototypeDemo();
 instance.setAttr(this.attr);
 return instance;
 }
}

利点

  • すべての言語で実現可能です。

デメリット

  • コードが複雑になります。新規作成と代入の方法によって、もし属性がたくさんあれば、たくさんのコードになるはずですが、効率はあまり高くありません。

Javaのクローナブルを使う

JavaのObjectにはオブジェクトの割り当てを可能にするcloneメソッドがありますが、cloneを使用するクラスはCloneableインターフェイスを実装する必要があります。CloneNotSupportedExceptionそうでないと例外がスローされます。

Javaの実装は以下の通りです:

public class JavaPrototypeDemo implements Cloneable {
 @Override
 protected JavaPrototypeDemo clone() {
 try {
 return (JavaPrototypeDemo) super.clone();
 } catch (CloneNotSupportedException e) {
 e.printStackTrace();
 return null;
 }
 }
}

利点

  • Javaを実装するのは簡単です。

デメリット

  • Javaのみ
  • ただの浅いコピーです

シナリオ

プロトタイプ・パターンは、主にオブジェクトを作成するプロセスを簡略化することができます。オブジェクトを作成するためのコストが大きい場合に適しています。操作のために、より複雑なオブジェクトのコピーが必要な場合、prototypeパターンを検討してみてください。

&

プロトタイプ・パターンには問題があります。

  • 浅いコピー:プロトタイプオブジェクトのプロパティもオブジェクトであり、コピーがプロパティのオブジェクト参照である場合、これは浅いコピーです。
  • ディープコピー:プロトタイプオブジェクトの属性もオブジェクトであり、属性オブジェクトをコピーすると、クローンオブジェクトにもコピーがコピーされ、これはディープコピーです。

言い換えれば、ディープ・レプリケーションはまさに完全なクローンです。浅い複製は一番外側の殻をコピーするだけです。

ディープ・レプリケーションの実装

ディープ・コピーを行うには、内部参照データ型のプロパティの新しいコピーも作成する必要があります。各参照オブジェクトまで再帰的に作業し、それを代入する前にコピーを作成することは可能ですが、それはあまりにも面倒で非効率的です。オブジェクトのバイナリ・ストリームをメモリに書き込み、ストリームからオブジェクトをデシリアライズします。

//  
@Data
class SerialObj implements Serializable {
 private String name;
 private Attachment attachment;
 public SerialObj deepClone() throws IOException, ClassNotFoundException {
 // オブジェクトをストリームに書き込む
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(baos);
 oos.writeObject(this);
 // ストリームからオブジェクトを取り出す
 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
 ObjectInputStream ois = new ObjectInputStream(bais);
 return (SerialObj) ois.readObject();
 }
}
// 組み込みの組み合わせオブジェクト
class Attachment implements Serializable {
 private String name;
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
}

利点

  • ディープコピー可能

デメリット

  • 少しコードが重くなりますが、必要なときに使える唯一のものです。

プロトタイプマネージャー

クライアントが使用できるように、複数のプロトタイプオブジェクトをコレクションに格納します。これはファクトリーに相当するもので、ファクトリーの内部にはすでにたくさんの型があり、オブジェクトが欲しい人がこのファクトリーに言及すると、ファクトリーは自分の型を与えず、この型のコピーをクローンしてくれます。プロトタイプマネージャーはプロトタイプクラスのファクトリーに相当します。

要するに、プロトタイプ・オブジェクトの束を保持するプロトタイプ・マネージャーを作成し、外の世界はプロトタイプ・オブジェクトを直接操作する代わりに、プロトタイプ・マネージャーを経由してプロトタイプ・オブジェクトをクローンします。

ユーエムエル

ものだね

  1. プロトタイプ・オブジェクトの統一インターフェース
  2. プロトタイプオブジェクト
  3. プロトタイプ・オブジェクト・マネージャ・クラスでは、このファクトリーは一般的にシングルトンでなければなりません。

実装

//1: このマネージャーが管理するプロトタイプクラスへのパブリックインターフェース。
interface IObj extends Cloneable {
 IObj clone();
 void say();
}
//2: プロトタイプクラスの定義、cloneは自分で実装する必要がある。
class Obj1 implements IObj {
 @Override
 public IObj clone() {
 try {
 return (IObj) super.clone();
 } catch (CloneNotSupportedException e) {
 e.printStackTrace();
 return null;
 }
 }
 @Override
 public void say() {
 System.out.println("i am 1");
 }
}
class Obj2 implements IObj {
 @Override
 public IObj clone() {
 try {
 return (IObj) super.clone();
 } catch (CloneNotSupportedException e) {
 e.printStackTrace();
 return null;
 }
 }
 @Override
 public void say() {
 System.out.println("i am 2");
 }
}
//3: プロトタイプ・オブジェクト・マネージャー
class PrototypeManager {
 /*ハングリーマンスタイルのシングルトンを使う。*/
 private HashMap<String, IObj> hm = new HashMap<>();
 private static final PrototypeManager manager = new PrototypeManager();
 private PrototypeManager() {
 hm.put("1", new Obj1());
 hm.put("2", new Obj2());
 }
 public IObj getObj(String key) {
 return (hm.get(key)).clone();
 }
 public static PrototypeManager getManager() {
 return manager;
 }
}

アタッチメント

関連コード:

また、コードや記事に問題があれば修正してください!ありがとうございました!

Read next