概念
プロトタイプパターンとは、プロトタイプインスタンスによって作成されたオブジェクトのタイプを指し、これらのプロトタイプをコピーして新しいオブジェクトを作成します。これは、作成者パターンに属します。
プロトタイプパターンには、主に次の3つの役割があります。
- クライアント:クライアントクラスは、オブジェクトを作成するためのリクエストを提供します
- 抽象プロトタイプ(プロトタイプ):コピーインターフェイスを指定します
- 具体的なプロトタイプ:コピーされるオブジェクト
注:新しいキーワードの代わりにオブジェクトコピーを使用してオブジェクトを作成するモードは、プロトタイプモードと呼ばれます。
成し遂げる
浅いクローン
標準的な書き込み
1.プロトタイプインターフェイスを作成します
public interface IPrototype<T> {
T clone();
}
2.具体的なプロトタイプを作成します
public class ConcretePrototype implements IPrototype {
int age;
String name;
@Override
public String toString() {
return "ConcretePrototype{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public ConcretePrototype clone() {
ConcretePrototype cp=new ConcretePrototype();
cp.setAge(this.age);
cp.setName(this.name);
return cp;
}
}
3.テスト
public class PrototypeTest {
public static void main(String[] args) {
//创建原型
ConcretePrototype cp =new ConcretePrototype();
cp.setName("tom");
cp.setAge(21);
System.out.println(cp);
//拷贝原型
ConcretePrototype cloneType=cp.clone();
System.out.println(cloneType);
}
}
操作結果:現時点
では、属性をオブジェクトにコピーするだけではないかとの質問がありましたが、オリジナルと大差ありません。確かに、上記の割り当てプロセスは自分で行います。実際のコーディングでは、通常、この種の物理的な労力を無駄にすることはありません。
次に、プロトタイプモードを作成する別の方法を見ていきます。
JDKにCloneableインターフェースを実装する
JDKは、スレッドAPIの実装を支援してくれました。必要なのは、Cloneableインターフェイスインターフェイスのみです。
1.具体的なプロトタイプを作成する
public class ConcretePrototype implements Cloneable {
int age;
String name;
@Override
public String toString() {
return "ConcretePrototype{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public ConcretePrototype clone() {
try {
return (ConcretePrototype)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
2.テスト
public class PrototypeTest {
public static void main(String[] args) {
//创建原型
ConcretePrototype cp =new ConcretePrototype();
cp.setName("tom");
cp.setAge(21);
System.out.println(cp);
//拷贝原型
ConcretePrototype cloneType=cp.clone();
System.out.println(cloneType);
}
}
操作結果:
次に、ConcretePrototypeクラスに趣味の属性を追加します。
public class PrototypeTest {
public static void main(String[] args) {
//创建原型
ConcretePrototype cp =new ConcretePrototype();
cp.setName("tom");
cp.setAge(21);
List<String>hobbies=new ArrayList<String>();
hobbies.add("书法");
hobbies.add("绘画");
cp.setHobbies(hobbies);
//拷贝原型
ConcretePrototype cloneType=cp.clone();
cloneType.getHobbies().add("游泳");
System.out.println(cp);
System.out.println(cloneType);
}
}
結果:
コピーしたオブジェクトに趣味を追加した後、プロトタイプオブジェクトも変更されていることがわかりました。クローン化されたオブジェクトとプロトタイプオブジェクトが接続のない2つの独立したオブジェクトであることが望まれるため、これは明らかに私たちの期待を満たしていません。分析の結果、趣味がアドレスを共有していることがわかりました。つまり、参照されているアドレスがコピーされたということです。これは私たちがしばしば浅いクローンと呼ぶものです
ディープクローン
シリアル化によって実現:
public class ConcretePrototype implements Serializable {
int age;
String name;
List<String>hobbies;
@Override
public String toString() {
return "ConcretePrototype{" +
"age=" + age +
", name='" + name + '\'' +
", hobbies=" + hobbies +
'}';
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ConcretePrototype deepClone() {
try {
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
return (ConcretePrototype)ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public class PrototypeTest {
public static void main(String[] args) {
//创建原型
ConcretePrototype cp =new ConcretePrototype();
cp.setName("tom");
cp.setAge(21);
List<String>hobbies=new ArrayList<String>();
hobbies.add("书法");
hobbies.add("绘画");
cp.setHobbies(hobbies);
//拷贝原型
ConcretePrototype cloneType=cp.deepClone();
cloneType.getHobbies().add("游泳");
System.out.println(cp);
System.out.println(cloneType);
}
}
操作結果:
ディープクローニングはシングルトンを破壊し、解決策は非常に簡単です:ディープクローニングを禁止します。シングルトンクラスがCloneableインターフェイスを実装していないか、== clone()と記述します。の方法シングルトンオブジェクトは、clone()==メソッドで返すことができます。
ソースコードで使用
最初にCloneableインターフェースを見てみましょう。
public interface Cloneable {
}
Cloneableインターフェイスは非常にシンプルです。その実装クラスを見てみましょう。たとえば、ArrayListクラスの実装は次のとおりです。
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
総括する
利点
- 改良された性能。オブジェクトのパフォーマンスは、直接新しいものよりも大幅に向上しています
- オブジェクト作成プロセスの簡素化
不利益
- クラスごとにcloneメソッドを構成する必要があります
- 既存のクラスを変更する場合は、コードを変更する必要があります。これは、開始と終了の原則に違反します。