初心者のためのJavaデザインパターンの注意事項 - プロトタイプパターン

プロトタイプモードはオリジナルモデルモードとも呼ばれます。

 

1. 目的:
プロトタイプ インスタンスを使用して、作成するオブジェクトのタイプを指定し、これらのプロトタイプをコピーして新しいオブジェクトを作成します。

 

2. 参加者:
• 抽象プロトタイプ (プロトタイプ):それ自体をクローンするインターフェイスを宣言します。
•具体的なプロトタイプ:自身のクローン作成操作を実装します。
• クライアント:プロトタイプ自体をクローンして新しいオブジェクトを作成します。

 

3. 構造:



 

 

定義上、プロトタイプ パターンはオブジェクトをコピーするために使用されます。

 

では、プロトタイプ モードをいつ使用する必要があるのでしょうか?

 

自動車生産の例を使って理解してみましょう。 

まず第一に、定義によれば、プロトタイプ モードはそれ自体をクローンする必要があるため、これは製品プロトタイプが存在する必要があることを意味します。

つまり、プロトタイプパターンのプロトタイプは、やはりファクトリメソッドなどで最初に作成する必要があります。

 

この観点から、プロトタイプパターンは、複数の同一または類似の製品オブジェクトを生産するときに使用されます。

 

では、プロトタイプパターンを使用する利点は何でしょうか?

 

Audi A6 が生産されると仮定して、ファクトリー モード (またはビルダー モード) の使用を検討してください。

 

1. 私がアウディ A6 を所有していて、それはとても良いもので、私の友人もそれを欲しがっているとします。しかし、それがどの工場で生産されているのかも分からず、どこで買えばいいのかも分かりません。私のアウディ A6 のコピーを作成できれば、友人にとっては非常に便利ですし、お金もかからないでしょう。(これは良い夢です!!)

プロトタイプ モードを使用して、クローンを作成するだけです。

これにより、その製品がどこの工場で生産されたものであるかを知る必要がなく、コピーするだけですぐに新しい製品オブジェクトを得ることができます。

これにより、システムは製品の作成、構成、プレゼンテーションからより独立したものになりますなぜなら、それは製品そのものを実現するために自分自身で作成されているからです。

 

2. アウディ A8 など、他のモデルの車を再現する必要がある場合。

ファクトリーモードでは、通常、別のアウディA8工場を建設する必要があります。

 

Audi A8 と Audi A6 は同じ製品レベルであるため、Audi A6 製品オブジェクトを複製して一部の属性を変更すると、新しい製品オブジェクト Audi A8 を作成できます。この方法では、新しい工場は必要ありません。(メーカーも夢見てます!!)

つまり、これにより、製品クラス階層と並行してファクトリ クラス階層を作成することが回避されます。

 

 

 プロトタイプ モードの使用により、製品作成プロセスはより独立しており、対応するファクトリ クラスは必要なく、製品内のメソッドを使用して作成されます。したがって、元のシステムの構造に影響を与えることなく、新しい製品カテゴリを動的に追加したり、元の製品カテゴリを削減したりすることが容易になります。

 

 

プロトタイプモードを実装するにはどうすればよいですか?

プロトタイプの自己複製(クローン化)を実現することです。これには、シャロー クローン作成とディープ クローン作成の概念が含まれます: (Yan Hon 著「Java とパターン」から抜粋)

1. 浅いコピー (浅いクローン)
コピーされたオブジェクトのすべての変数には元のオブジェクトと同じ値が含まれており、他のオブジェクトへの参照はすべて元のオブジェクトを指し続けます。つまり、浅いコピーは、参照しているオブジェクトではなく、問題のオブジェクトのみをコピーします。

2. ディープ コピー (ディープ クローン)
コピーされたオブジェクトのすべての変数には、他のオブジェクトを参照する変数を除き、元のオブジェクトと同じ値が含まれます。他のオブジェクトを参照する変数は、元の参照オブジェクトではなく、コピーされた新しいオブジェクトを指します。つまり、ディープ コピーでは、コピー対象のオブジェクトが参照するすべてのオブジェクトがコピーされます。

 

3.シリアル化を使用してディープ コピーを実行し
、オブジェクトをストリームに書き込むプロセスがシリアル化のプロセスですが、Java プログラマの間では、これは非常に明確に「フリーズ」または「ピックリング」プロセスとも呼ばれています。; 並列化 (デシリアライゼーション) ストリームからオブジェクトを読み取るプロセスは、「解凍」または「デピッキング」プロセスと呼ばれます。ストリームに書き込まれているのはオブジェクトのコピーであり、元のオブジェクトは JVM にまだ存在していることに注意してください。したがって、「ピクルスに漬けられる」ものはオブジェクトのコピーにすぎず、Java ピクルスはまだ新鮮です。
Java 言語でオブジェクトをディープ コピーするには、多くの場合、まずオブジェクトに Serializable インターフェイスを実装させ、次にオブジェクト (実際にはオブジェクトの単なるコピー) をストリームに書き込み (ピクルにピクルし)、それから読み取ります。ストリームから取り出す (ピクルスをストリームに戻す)、新鮮)、オブジェクトを再構築できます。

 

Java の clone() メソッド:
clone メソッドは、オブジェクトのコピーをコピーし、呼び出し元に返します。一般的に言えば、 clone() メソッドは次の条件を満たします:
① 任意のオブジェクト x に対して、x.clone() が存在します !=x//複製されたオブジェクトは元のオブジェクトと同じオブジェクトではありません。
② 任意のオブジェクト x に対して、x が存在します。 clone ().getClass()= =x.getClass()//クローンされたオブジェクトは元のオブジェクトと同じ型を持ちます。
③ オブジェクト x の equals() メソッドが適切に定義されていれば、x.clone().equals( x) は true である必要があります。

Java でのオブジェクトのクローン作成
① オブジェクトのコピーを取得するには、Object クラスの clone() メソッドを使用します。
②基底クラスの clone() メソッドを派生クラスでオーバーライドし、public として宣言します。
③派生クラスの clone() メソッド内で super.clone() を呼び出します。
④派生クラスにCloneableインターフェースを実装します。

 

Java で実装されたコード:

浅いコピー (浅いクローン):

自動車カテゴリー:

 

/*
 * 汽车类
 * 
 * 浅克隆(shadow clone)的实现
 */
public class Car implements Cloneable {
	
//	汽车款式
	public int type;

//	引用的其他对象,汽车发动机
	public Engine engine;

	public Object clone() {
		
		Object clone = null;
		try {

//			这里只是克隆的自己本身
			clone = super.clone();
			
//			为什么在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?
//			因为,在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,
//			将原始对象的内容一一复制到新对象的存储空间中。	
			
		} catch (CloneNotSupportedException e) {

		}
		return clone;
	}

}

 

エンジン型式:

/*
 * 汽车发动机类,汽车类中引用的对象的类
 */
public class Engine implements Cloneable {
	
//	汽车发动机型号
	public int model;

}

 

クライアントの呼び出し:

/*
 * 浅克隆(shadow clone)的调用
 */
public class Client {
	   public static  void main(String argv[])
	   {
		 System.out.println("-----创建汽车1-----");
		 Car car1=new Car();
	     car1.engine=new Engine();
	     car1.type=1;
	     car1.engine.model=1;
	     System.out.println("汽车1款式:"+car1.type+"\t汽车1的发动机型号:"+car1.engine.model);
	     
	     System.out.println("----汽车1--克隆-->汽车2----");
	     Car car2=(Car)car1.clone();
	     car2.type=2;
	     car2.engine.model=2;
	     
	     System.out.println("汽车2款式:"+car2.type+"\t汽车2的发动机型号:"+car2.engine.model);
	     System.out.println("汽车1款式:"+car1.type+"\t汽车1的发动机型号:"+car1.engine.model);
	   }
	 }

 

操作結果:

-----创建汽车1-----
汽车1款式:1	汽车1的发动机型号:1
----汽车1--克隆-->汽车2----
汽车2款式:2	汽车2的发动机型号:2
汽车1款式:1	汽车1的发动机型号:2

 

実行結果によると、浅いコピー (浅いクローン) は検討中のオブジェクトのみをコピーし、参照しているオブジェクトはコピーしないことがわかります。したがって、 2号車エンジン型式を
2にすると、1号車のエンジン型式も1から2に変更される

1号車と2号車は実は同じエンジンを使っているので

 

ディープコピー (ディープクローン):

ディープ コピー (ディープ クローン作成) を実装するにはどうすればよいですか? 車のクラスと車のエンジン クラスを変更します。

 

自動車カテゴリー:

/*
 * 深克隆
 */
public class Car implements Cloneable {
	
	public int type;

//	引用的其他对象
	public Engine engine;

	public Object clone() {

		Car temp = null;
		try {

//			先把自己本身复制
			temp = (Car) super.clone();
			
//			为了实现深度克隆,需要将对其他对象(在这里是engine)的引用都复制过去。
			temp.engine = (Engine) engine.clone();
		} catch (CloneNotSupportedException e) {
			// should never happen
		}

		return temp;
	}
}

 

エンジン型式:

public class Engine implements Cloneable {
	public int model;

	/**
	 * 为了实现深度克隆,需要给在Lay1中被应用的对象lay2)也提供一个自己克隆自身的方法
	 */
	public Object clone() {
		Object clone = null;
		try {
			clone = super.clone();
		} catch (CloneNotSupportedException e) {

		}
		return clone;
	}

}

 

クライアントの呼び出しは変更されません。

 

操作結果:

-----创建汽车1-----
汽车1款式:1	汽车1的发动机型号:1
----汽车1--克隆-->汽车2----
汽车2款式:2	汽车2的发动机型号:2
汽车1款式:1	汽车1的发动机型号:1

 

実行結果によると、ディープコピー(ディープクローニング)では、コピー対象のオブジェクトが参照するオブジェクトがすべてコピーされていることがわかります。

したがって、 2号車エンジン型式を2とした場合1号車のエンジン型式は1のまま変化しません

1号車と2号車には異なるエンジンが搭載されているためです。

 

シリアル化を使用してディープ コピー (ディープ クローン) を実行します。

 

自動車

シリアル化インターフェイスを実装し、ストリーム内のオブジェクトの読み取りと書き込みを使用しました。

 

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/*
 * 汽车类
 * 
 * 序列化的方式实现深克隆(deep clone)
 */
public class Car implements Serializable {

	/**
	 * serialVersionUID 
	 */
	private static final long serialVersionUID = 1859639569305572020L;

//	汽车款式
	public int type;

//  引用的其他对象,汽车发动机
	public Engine engine;

	public Object clone() {

		try {

//			将对象写到流里,把对象写到流里的过程是串行化(Serilization)过程

			ByteArrayOutputStream bo = new ByteArrayOutputStream();
			ObjectOutputStream oo;

			oo = new ObjectOutputStream(bo);

			oo.writeObject(this);

//			从流里读出来,把对象从流中读出来的并行化(Deserialization)过程
			ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
			ObjectInputStream oi = new ObjectInputStream(bi);
			return (oi.readObject());

		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			return null;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			return null;
		}
	}
}

 

 

自動車エンジンカテゴリ:

 

import java.io.Serializable;

public class Engine implements Cloneable, Serializable {

	/**
	 * Car对象以及对象内部所有引用到的对象engine都是可串行化的
	 */
	private static final long serialVersionUID = -6228724315977120960L;

	public int model;

}

 

 

クライアントの呼び出しは変更されません。

 

操作結果:

-----创建汽车1-----
汽车1款式:1	汽车1的发动机型号:1
----汽车1--克隆-->汽车2----
汽车2款式:2	汽车2的发动机型号:2
汽车1款式:1	汽车1的发动机型号:1

 

実行結果によると、シリアル化によってディープ コピー (クローン可能性) も実現されていることがわかります。

したがって、 2号車エンジン型式を2とした場合1号車のエンジン型式は1のまま変化しません

1号車と2号車には異なるエンジンが搭載されているためです。

おすすめ

転載: blog.csdn.net/louis_lee7812/article/details/83778148