JAVAデザインモデル--プロトタイプモデル


目次
一、プロトタイプモードとは
二、原型モード実現
単純な形式
登録形式
三、Javaにおける深いコピーと浅いコピー(または深いクローンと浅いクローン)
四、原型モード適用シーン
五、原型パターンの特徴
参考記事
一、プロトタイプモードとは
プロトタイプ(Prototype)モードは、プロトタイプインスタンスを使用して作成オブジェクトの種類を指定し、プロトタイプインスタンスをコピーする方法で新しいオブジェクトを作成するオブジェクト作成モードです.したがって、プロトタイプインスタンスを使用して作成されたインスタンスは、プロトタイプインスタンスと同じデータを有します.
二、原型モード実現
プロトタイプモードは主にオブジェクトのレプリケーションに使用され、Prototypeクラスには次の2つの条件()が必要です.-Cloneableインタフェースを実装します.Java言語にはCloneableインタフェースがあり、実行時に仮想マシンがこのインタフェースを実現したクラスでcloneメソッドを安全に使用できることを通知する役割は1つしかありません.Java仮想マシンでは、このインタフェースを実装したクラスのみがコピーできます.そうしないと、実行時にCloneNotSupportedException異常が放出されます.-Objectクラスのcloneメソッドを書き換えます.Javaでは、すべてのクラスの親クラスがObjectクラスであり、Objectクラスにはcloneメソッドがあり、オブジェクトのコピーを返す役割を果たすが、その役割ドメインprotectedタイプの場合、一般的なクラスは呼び出せないため、Prototypeクラスはcloneメソッドを書き換え、メソッドの役割ドメインをpublicタイプに変更する必要がある.
プロトタイプモードは比較的簡単なモードであり、非常に理解しやすく、インタフェースを実現し、一つの方法を書き換えることでプロトタイプモードを完了した.実際の応用では、プロトタイプモードは単独ではあまり現れず、他のモードと混用されることが多く、彼のプロトタイプクラスPrototypeも抽象クラスで代替されることが多い.
プロトタイプパターンの表現形式:(1)単純形式(2)登録形式
単純な形式
Prototypeクラスが抽象の場合、サンプルコードは以下の通りです.
抽象プロトタイプロール:Cloneableインタフェースを実装し、サブクラスに共通のメソッドを提供する必要があります.
public abstract class Prototype implements Cloneable {
	/**
	 *            
	 */
	protected abstract Prototype clone();
	/**
	 *           ,    Prototype    ,    
	 */
	protected abstract String getName();
	protected abstract void setName(String name);
}

具体的なプロトタイプロール:抽象プロトタイプクラスから継承し、親クラスの抽象メソッドを実装する必要があります.
public class ConcretePrototypeA extends Prototype {
	/**
	 *  ConcretePrototypeA       name  ,    
	 */
	protected String name;
	@Override
	public String getName() {
		return name;
	}
	@Override
	public void setName(String name) {
		this.name = name;
	}
	/**
	 *   clone  
	 */
	@Override
	public Prototype clone() {
		Prototype prototype = new ConcretePrototypeA();
		prototype.setName(this.name);
		System.out.println("ConcretePrototypeA    !");
		return prototype;
	}
}
public class ConcretePrototypeB extends Prototype {
	/**
	 *  ConcretePrototypeA       name  ,    
	 */
	protected String name;
	@Override
	public String getName() {
		return name;
	}
	@Override
	public void setName(String name) {
		this.name = name;
	}
	/**
	 *   clone  
	 */
	@Override
	protected Prototype clone() {
		Prototype prototype = new ConcretePrototypeB();
		prototype.setName(this.name);
		System.out.println("ConcretePrototypeB    !");
		return prototype;
	}
}

クライアントテストを作成します.
public class Client{
	public static void main(String[] args) {
		/**
		 *     ,           ,      Prototype        
		 */
		Prototype prototypeA=null;
		Prototype prototypeB=null;
		/**
		 *          
		 */
		prototypeA=new ConcretePrototypeA();
		prototypeA.setName("A1");
		/**
		 *          
		 */
		prototypeB=new ConcretePrototypeB();
		prototypeB.setName("B1");
		/**
		 *     
		 */
		Prototype copyA=prototypeA.clone();
		Prototype copyB=prototypeB.clone();
		/**
		 *                  
		 */
		System.out.println("  A   :"+prototypeA.getName());
		System.out.println("  B   :"+prototypeB.getName());
		System.out.println("  A   :"+copyA.getName());
		System.out.println("  B   :"+copyB.getName());
		/**
		 *              
		 */
		copyA.setName("  A1");
		System.out.println("  A   :"+copyA.getName());	
		System.out.println("  A   :"+prototypeA.getName());	
	}
}

実行プログラムの印刷結果は次のとおりです.
ConcretePrototypeA    !
ConcretePrototypeB    !
  A   :A1
  B   :B1
  A   :A1
  B   :B1
  A   :  A1
  A   :A1

テスト結果から,コピーAのname属性を修正してもプロトタイプオブジェクトには影響せず,コピーAは確かにコピーされた新しいオブジェクトであることが分かる.
プロトタイプモードを使用してオブジェクトを作成するには、このインスタンス自体のタイプに関心を持つ必要はありません.cloneメソッドが実装されている限り、newで作成することなく、このメソッドで新しいオブジェクトを取得できます.
登録形式
この形式と単純な形式の違いは、コピーするプロトタイプオブジェクトを登録管理するPrototypeManager(プロトタイプマネージャ)ロールを追加することであり、このロールは外部に新しいプロトタイプオブジェクトを追加し、登録したプロトタイプオブジェクトを取得するために必要な方法を提供する.
この形式の抽象プロトタイプキャラクタPrototype、具体的なプロトタイプキャラクタConcretePrototypeA、具体的なプロトタイプキャラクタConcretePrototypeBは単純な形式と全く同じである.
プロトタイプマネージャの役割:コピーするプロトタイプをMapをコンテナとして保存登録します.PrototypeManagerのサンプルコードは次のとおりです.
import java.util.HashMap;
import java.util.Map;
public class PrototypeManager {
	/**
	 *    Map  ,                  
	 */
	private static Map map = new HashMap();
	/**
	 *        ,         
	 */
	private PrototypeManager() {
	}
	/**
	 *                             
	 * 
	 * @param prototypeIndex
	 *                
	 * @param prototype
	 *                
	 */
	public synchronized static void setPrototype(String prototypeIndex,
			Prototype prototype) {
		map.put(prototypeIndex, prototype);
	}
	/**
	 *                     
	 * 
	 * @param prototypeIndex
	 *                
	 */
	public synchronized static void removePrototype(String prototypeIndex) {
		map.remove(prototypeIndex);
	}
	/**
	 *                
	 * 
	 * @param prototypeIndex
	 *                
	 * @return            
	 * @throws Exception
	 *                           ,     
	 */
	public synchronized static Prototype getPrototype(String prototypeIndex)
			throws Exception {
		Prototype prototype = map.get(prototypeIndex);
		if (prototype == null) {
			throw new Exception("        !");
		}
		return prototype;
	}
}

クライアントをもう1つ作成してテストします.
public class MainClass {
	public static void main(String[] args) throws Exception {
		/**
		 *     ,           ,      Prototype        
		 */
		Prototype prototypeA = null;
		Prototype prototypeB = null;
		/**
		 *          
		 */
		prototypeA = new ConcretePrototypeA();
		prototypeA.setName("A");
		/**
		 *          
		 */
		prototypeB = new ConcretePrototypeB();
		prototypeB.setName("B");
		/**
		 *           PrototypeManager     
		 */
		PrototypeManager.setPrototype("A", prototypeA);
		PrototypeManager.setPrototype("B", prototypeB);
		/**
		 *   PrototypeManager                    name   "A"           
		 */
		Prototype copyA = PrototypeManager.getPrototype("A").clone();
		/**
		 *   PrototypeManager                    name   "B"           
		 */
		Prototype copyB = PrototypeManager.getPrototype("B").clone();
		/**
		 *                    
		 */
		System.out.println("  A   :" + prototypeA.getName());
		System.out.println("  B   :" + prototypeB.getName());
		System.out.println("  A   :" + copyA.getName());
		System.out.println("  B   :" + copyB.getName());
		/**
		 *              
		 */
		copyA.setName("  A1");
		System.out.println("  A   :" + copyA.getName());
		System.out.println("  A   :" + prototypeA.getName());
	}
}

実行プログラムの印刷結果は次のとおりです.
ConcretePrototypeA    !
ConcretePrototypeB    !
  A   :A
  B   :B
  A   :A
  B   :B
  A   :  A1
  A   :A

登録形式のプロトタイプモードを使用すると、コピーするプロトタイプインスタンスの種類が多く、使用するプロトタイプインスタンスが頻繁に変化する場合に便利です.プロトタイプインスタンスを作成し、プロトタイプマネージャに登録すれば(自分で区別できる名前をインデックスに)、どのプロトタイプをコピーするかはマネージャから直接取得します. 
三、Javaにおける深いコピーと浅いコピー(または深いクローンと浅いクローン)
Javaのすべてのクラスはjavaからです.lang.Objectクラスは継承され、Objectクラスはprotected Object clone()メソッドを提供してオブジェクトをコピーしますが、Objectクラスのcloneメソッドはオブジェクトの基本的なデータ型のみをコピーし、配列、コンテナオブジェクト、参照タイプオブジェクトなどはコピーしません.これがいわゆる浅いコピーです.深いコピーを実行するには、プロトタイプモードの配列、コンテナオブジェクト、参照オブジェクトなどを別途コピーする必要があります.
注意事項:-Java言語で提供されるCloneableインタフェースは、実行時にJava仮想マシンがこのクラスでclone()メソッドを安全に使用できることを通知するだけの役割を果たします.このclone()メソッドを呼び出すことで、オブジェクトのコピーを得ることができます.Objectクラス自体がCloneableインタフェースを実装していないため、考慮したクラスがCloneableインタフェースを実装していない場合、clone()メソッドを呼び出すと、CloneNotSupportedException例外が放出されます.-プロトタイプ・モードを使用してオブジェクトをコピーしても、クラスの構築方法は呼び出されません.オブジェクトのコピーは、Objectクラスのcloneメソッドを呼び出すことによって行われるため、メモリに直接データをコピーするため、クラスの構築メソッドは呼び出されません.構築メソッドのコードは実行されないだけでなく、アクセス権限もプロトタイプ・モードに対して無効です.単例モードを覚えていますか?シングル・インスタンス・モードでは、構築メソッドのアクセス権限をprivate型に設定すれば、シングル・インスタンスを実現できます.ただしcloneメソッドは構造メソッドの権限を直接無視するので,単例モードとプロトタイプモードは衝突しており,使用時には特に注意しなければならない.
オブジェクトをストリームに書き込むプロセスはシーケンス化(Serialization)プロセスである.オブジェクトをストリームから読み出すプロセスを逆シーケンス化(Deserialization)プロセスと呼ぶ.ストリームに書かれているのはオブジェクトのコピーであり,元のオブジェクトはJVMに存在することを指摘すべきである.
Java言語でオブジェクトを深くコピーすると、オブジェクトにSerializableインタフェースを実装させ、オブジェクト(実際にはオブジェクトのコピーのみ)をストリームに書き(シーケンス化)、ストリームから読み返し(逆シーケンス化)すると、オブジェクトを再構築できます.
シーケンス化による深度クローンの実装:まず、Serializableインタフェースを実装するPersonクラスを作成し、深いコピーであるかどうかをテストするためにListコンテナ属性familyを追加します.コードは次のとおりです. 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.List;
@SuppressWarnings("serial")
public class Person implements Serializable{
	//   
	private String name;
	//     
	private List family;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public List getFamily() {
		return family;
	}
	public void setFamily(List family) {
		this.family = family;
	}
	public Person serializationClone() throws IOException,
			ClassNotFoundException {
		//        
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(bos);
		oos.writeObject(this);
		//       
		ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
		ObjectInputStream ois = new ObjectInputStream(bis);
		return (Person) ois.readObject();
	}
}


クライアントクラスを作成してテストします.
import java.util.ArrayList;
import java.util.List;
public class MainClass {
	public static void main(String[] args) throws Exception {
		/**
		 *           ,     
		 */
		Person person = new Person();
		/**
		 *            
		 */
		person.setName("demo");
		/**
		 *             ,   serializationClone()               
		 */
		List family = new ArrayList();
		family.add("wife");
		family.add("child");
		person.setFamily(family);
		/**
		 *   serializationClone()      
		 */
		Person copyPerson=person.serializationClone();
		/**
		 *                  
		 */
		System.out.println("       :"+person.getName());
		System.out.println("       :"+person.getFamily());
		System.out.println("       :"+copyPerson.getName());
		System.out.println("       :"+copyPerson.getFamily());
		/**
		 *              ,                
		 */
		copyPerson.setName("copy-demo");
		List copyFamily = new ArrayList();
		copyFamily.add("copy-wife");
		copyFamily.add("copy-child");
		copyPerson.setFamily(copyFamily);
		/**
		 *                
		 */
		System.out.println("          :"+copyPerson.getName());
		System.out.println("          :"+copyPerson.getFamily());
		System.out.println("       :"+person.getName());
		System.out.println("       :"+person.getFamily());
	}
}

実行プログラムの印刷結果は次のとおりです.
       :demo
       :[wife, child]
       :demo
       :[wife, child]
          :copy-demo
          :[copy-wife, copy-child]
       :demo
       :[wife, child]

大きな成果を収め、personプロトタイプオブジェクトは完璧な深さでコピーされた.
このようにする前提は、オブジェクトおよびオブジェクト内部のすべての参照オブジェクトがシーケンス可能であることです.そうしないと、シーケンス不可能なオブジェクトがtransientに設定されているかどうかをよく考察し、レプリケーションプロセスから除外する必要があります.
Java言語のすべてのクラスがclone()メソッドを継承するため、浅いコピーは深いコピーよりも明らかに実現しやすい.このclone()メソッドが行うのは浅いコピーである.
スレッド(Thread)オブジェクトやSocketオブジェクトのようなオブジェクトは、簡単にコピーしたり共有したりすることはできません.浅いクローンを使用しても深いクローンを使用しても、このような間接オブジェクトに関連する限り、間接オブジェクトをtransientに設定してコピーしない必要があります.あるいは、プログラムが独自に相当する同種のオブジェクトを作成し、コピーとして使用します.
四、原型モード適用シーン
プロトタイプ・モードの使用は、次の場合に考えられます.
-オブジェクトを作成するときは、作成されたオブジェクトだけがベースクラスの基本構造を継承するのではなく、プロトタイプオブジェクトのデータも継承します.
-ターゲットオブジェクトの修正が既存のプロトタイプオブジェクトに影響しないようにします(深度クローンの場合はまったく影響しません).
-クローン操作の詳細を非表示にします.多くの場合、オブジェクト自体のクローンはクラス自体のデータの詳細に関連する必要があります.   
五、原型パターンの特徴
1.プロトタイプオブジェクト自体からターゲットオブジェクトを作成します.すなわち,オブジェクト作成という動作はプロトタイプオブジェクト自体から生じる.
2.ターゲットオブジェクトは、プロトタイプオブジェクトのクローンです.つまり、Prototypeモードで作成したオブジェクトは、プロトタイプオブジェクトと同じ構造を持つだけでなく、プロトタイプオブジェクトと同じ値を持つ.
3.オブジェクトクローンの深さ階層によっては、浅さクローンと深さクローンがあります.
利点:-プロトタイプモードを使用してオブジェクトを作成すると、Objectクラスのcloneメソッドはローカルメソッドであり、メモリ内のバイナリストリームを直接操作し、特に大きなオブジェクトをコピーする場合、パフォーマンスの差が非常に顕著になるため、直接newオブジェクトよりもパフォーマンスが優れています.-プロトタイプ・モードを使用するもう1つの利点は、ドキュメントを編集するときのコピー・ペーストのように簡単にオブジェクトの作成を簡素化することです.
以上の利点から、類似オブジェクトを繰り返し作成する必要がある場合には、プロトタイプモードを使用することが考えられる.たとえば、1つのループ内にオブジェクトを作成する必要があります.オブジェクトの作成プロセスが複雑であるか、ループ回数が多い場合、プロトタイプモードを使用すると、作成プロセスを簡素化するだけでなく、システム全体のパフォーマンスを大幅に向上させることができます.
欠点:プロトタイプモードの最も主要な欠点は、各クラスにクローンメソッドを配備する必要があることです.クローン・メソッドを配備するには、クラスの機能を全般的に考慮する必要があります.これは、新しいクラスでは難しくありませんが、既存のクラスでは必ずしも容易ではありません.特に、クラス参照がシーケンス化された間接オブジェクトをサポートしていない場合、または循環構造を含む参照が含まれている場合です.  
参考記事
http://www.cnblogs.com/java-my-life/archive/2012/04/11/2439387.html
http://blog.csdn.net/jason0539/article/details/23158081