シーン
Java でのデザイン パターン - プロトタイプ パターン - 浅いクローンと深いクローンの使用例:
デザイン パターン - プロトタイプ パターン - Java でのシャロー クローニングとディープ クローニングの例 - プログラマが求めた
上にプロトタイプモードの使用例を最初に記録し、下に祝日やイベントを記録します。
メール送信例。
プロトタイプパターン
プロトタイプ パターン (Prototype Pattern) の単純さは、シングルトン パターンとイテレータ パターンに次いで 2 番目です。シンプルだからこそ、さまざまなシーンで活用できます。
次のように定義されます。
プロトタイプのインスタンスを使用して作成するオブジェクトの種類を指定し、このプロトタイプをコピーして新しいオブジェクトを作成します。
(プロトタイプ インスタンスを使用して、作成するオブジェクトの種類を指定し、それらのプロトタイプをコピーして新しいオブジェクトを作成します。)
シンプル、とても簡単!プロトタイプ モードの中核は、オブジェクトがコピーされる clone メソッドであり、Java はマークを付けるための Cloneable インターフェイスを提供します。
このオブジェクトはコピーできますが、なぜ「マーキング」と呼ばれるのでしょうか? JDK のヘルプを開くと、Cloneable にはメソッドがなく、このインターフェイスは単なるマーカーであることがわかります。
JVM 内のこのマークが付いたオブジェクトのみをコピーできます。では、「コピーされる可能性がある」から「コピーできる」に変換するにはどうすればよいでしょうか?
このメソッドは clone() メソッドをオーバーライドするものです。はい、そのとおりです。clone() メソッドをオーバーライドするものです。
プロトタイプパターンの長所
素晴らしい演技
プロトタイプ モードはメモリ バイナリ ストリームのコピーであり、特にループ本体で多数のオブジェクトが生成される場合、オブジェクトを直接新規作成するよりもパフォーマンスが大幅に向上します。
プロトタイプパターンは、その利点をよりよく反映できます。
コンストラクター制約をエスケープする
これは利点でもあり欠点でもあり、メモリに直接コピーするとコンストラクターは実行されません**。メリットとしては制約が減るということですが、
欠点は制約が減少することでもあり、実際のアプリケーションではこれを考慮する必要があります。
プロトタイプパターンの利用シナリオ
リソース最適化シナリオ
クラスの初期化では、データやハードウェア リソースなどの多くのリソースを消化する必要があります。
パフォーマンスとセキュリティ要件のあるシナリオ
new を使用してオブジェクトを生成するには、非常に面倒なデータの準備やアクセス権が必要となるため、プロトタイプ モードを使用できます。
1 つのオブジェクトに複数のモディファイアを使用するシナリオ
オブジェクトが他のオブジェクトからアクセスする必要があり、各呼び出し元がその値を変更する必要がある場合は、プロトタイプ モードを使用して、呼び出し元が使用できるように複数のオブジェクトをコピーすることを検討できます。
実際のプロジェクトでは、プロトタイプパターンが単独で現れることは少なく、通常はファクトリメソッドパターンと一緒に現れ、cloneメソッドでオブジェクトを作成し、
その後、ファクトリ メソッドによって呼び出し元に提供されます。
プロトタイプパターンについての注意事項
コンストラクターは実行されません
Cloneableを実装しcloneメソッドを書き換えたクラスAは引数なし構造またはパラメータ構造Bを持ち、newキーワードによりオブジェクトSを生成する。
次に、S.clone() によって新しいオブジェクト T が生成され、オブジェクトがコピーされるときにコンストラクター B は実行されません。詳細についてはサンプルコードを参照してください。
浅いコピーと深いコピー
注: 深いコピーと浅いコピーを混合しないことをお勧めします。特にクラスの継承に関しては、親クラスに複数の参照がある状況は非常に複雑です。
推奨される解決策は、ディープ コピーとシャロー コピーを別々に実装することです。
浅いコピーと深いコピーについてはサンプルコードを参照してください。
クローンと最後の2人の敵
オブジェクトのクローンは、オブジェクト内のfinalキーワードと競合します。cloneメソッドを使用する場合は、クラスのメンバー変数にfinalキーワードを追加しないでください。
ノート:
ブログ:
横柄なローグ気質 blog_CSDN ブログ - C#、アーキテクチャ ロード、SpringBoot のブロガー
達成
1.バンクフェスティバルは抽選通知メールを送信します。
新しい広告レターのテンプレート コード
@Data
public class AdvTemplate {
//广告信名称
private String advSubject = "xx银行抽奖活动";
//广告信内容
private String advContext = "抽奖活动通知:......";
}
2. 新しいメールクラスコード
//邮件类代码
@Data
public class Mail {
//收件人
private String receiver;
//邮件名称
private String subject;
//称谓
private String appellation;
//内容
private String context;
//邮件底部,一般是加上"xxx版权所有"等信息
private String tail;
public Mail(AdvTemplate advTemplate){
this.context = advTemplate.getAdvContext();
this.subject = advTemplate.getAdvSubject();
}
}
3.シーンカテゴリー
//场景类
public class Client {
//发送账单的数量
private static int MAX_COUNT = 6;
public static void main(String[] args) {
//模拟发送邮件
int i =0;
//把模板定义出来,这个一般从数据库中获取
Mail mail = new Mail(new AdvTemplate());
mail.setTail("badaoxxx版权所有");
while (i<MAX_COUNT){
//每封邮件不同的地方
mail.setAppellation(i+"先生/女士");
mail.setReceiver(i+"[email protected]");
sendMail(mail);
i++;
}
}
public static void sendMail(Mail mail){
System.out.println("标题:"+mail.getSubject()+"\t收件人:"+mail.getReceiver()+"\t...发送成功!");
}
}
質問:
シングルスレッド送信の場合、sendMail をマルチスレッドに変更する必要があるため、問題が発生します。最初のメール オブジェクトが生成され、スレッド 1 で実行されますが、まだ送信されていません。
スレッド 2 も開始され、メール オブジェクトの受信者とタイトルが直接変更されます。スレッドは安全ではありません。もちろん、他にも多くの解決策があります。
その 1 つは、この問題を解決するために新しいタイプのパターンを使用することです。つまり、オブジェクトのコピー関数を使用してこの問題を解決します。
4. メールクラスの変更
//修改后的邮件类
@Data
public class MailExtend implements Cloneable{
//收件人
private String receiver;
//邮件名称
private String subject;
//称谓
private String appellation;
//内容
private String context;
//邮件底部,一般是加上"xxx版权所有"等信息
private String tail;
public MailExtend(AdvTemplate advTemplate){
this.context = advTemplate.getAdvContext();
this.subject = advTemplate.getAdvSubject();
}
@Override
public MailExtend clone() throws CloneNotSupportedException {
MailExtend mailExtend = null;
mailExtend = (MailExtend) super.clone();
return mailExtend;
}
}
5. シーンクラスの変更
//修改后的场景类
public class ClientExtend {
//发送账单的数量
private static int MAX_COUNT = 6;
public static void main(String[] args) throws CloneNotSupportedException {
//模拟发送邮件
int i =0;
//把模板定义出来,这个一般从数据库中获取
MailExtend mail = new MailExtend(new AdvTemplate());
mail.setTail("badaoxxx版权所有");
while (i<MAX_COUNT){
//每封邮件不同的地方
MailExtend cloneMail = mail.clone();
mail.setAppellation(i+"先生/女士");
mail.setReceiver(i+"[email protected]");
sendMail(cloneMail);
i++;
}
}
public static void sendMail(MailExtend mail){
System.out.println("标题:"+mail.getSubject()+"\t收件人:"+mail.getReceiver()+"\t...发送成功!");
}
}
実行結果は変わらず、sendMail がマルチスレッドであっても問題ありません。
Clientクラスのmail.clone()は、オブジェクトをコピーして元のオブジェクトと同じ新しいオブジェクトを生成し、詳細データを変更します。
タイトルや宛先の設定など。new キーワードによるオブジェクトの生成ではなく、オブジェクトのコピーによって実現されるこのモードをプロトタイプモードと呼びます。
6. クローン後のコンストラクターが実行されないことを確認します。
//构造函数不会被执行
public class Thing implements Cloneable{
public Thing(){
System.out.println("构造函数被执行了...");
}
@Override
public Thing clone() throws CloneNotSupportedException {
Thing thing = null;
thing = (Thing)super.clone();
return thing;
}
}
オブジェクトをコピーする別のクライアントを作成する
public class TestClient {
public static void main(String[] args) throws CloneNotSupportedException {
Thing thing = new Thing();
Thing cloneThing = thing.clone();
}
}
実行結果から、オブジェクトのコピー時にコンストラクターが実行されていないことがわかります。
Object クラスの clone メソッドの原理は、メモリからバイナリ ストリームの形式でコピーし、メモリ ブロックを再割り当てすることです。
7. 浅いクローン作成と深いクローン作成
シャロークローン作成の例を見る
public class QianClone implements Cloneable{
//定义一个私有变量
private ArrayList<String> arrayList = new ArrayList<>();
@Override
public QianClone clone() throws CloneNotSupportedException {
QianClone thing = null;
thing = (QianClone)super.clone();
return thing;
}
public void setValue(String value){
this.arrayList.add(value);
}
public ArrayList<String> getValue(){
return this.arrayList;
}
}
シャロー クローンと呼ばれるシーン クラス
public class QianCloneClient {
public static void main(String[] args) throws CloneNotSupportedException {
QianClone qianClone = new QianClone();
qianClone.setValue("张三");
QianClone qianCloneClone = qianClone.clone();
qianCloneClone.setValue("李四");
System.out.println(qianClone.getValue());
//[张三, 李四]
}
}
プライベート変数 arrayList を追加し、その結果を Zhang San だけでなく Li Si も実行します。
これは、Object クラスの clone メソッドがオブジェクトをコピーするだけで、オブジェクト内の配列や参照オブジェクトはコピーされず、元のオブジェクトの内部要素アドレスを指したままになるためです。
このコピーは浅いコピーです。2 つのオブジェクトはプライベート変数を共有しており、あなたが変更すれば誰でもそれを変更できます。では、なぜ Mail クラスで String 型が使用できるのかというと、
浅いコピーによって引き起こされる問題はありません。内部配列とオブジェクトはコピーされないため、他のプリミティブ型 int、long、cha などと String はコピーされます。
ディープコピーの例
//深拷贝
public class ShenClone implements Cloneable{
//定义一个私有变量
private ArrayList<String> arrayList = new ArrayList<>();
@Override
public ShenClone clone() throws CloneNotSupportedException {
ShenClone thing = null;
thing = (ShenClone)super.clone();
thing.arrayList = (ArrayList<String>) this.arrayList.clone();
return thing;
}
public void setValue(String value){
this.arrayList.add(value);
}
public ArrayList<String> getValue(){
return this.arrayList;
}
}
ディープコピーシーンクラス
public class ShenCloneClient {
public static void main(String[] args) throws CloneNotSupportedException {
ShenClone shenClone = new ShenClone();
shenClone.setValue("张三");
ShenClone shenClone1 = shenClone.clone();
shenClone1.setValue("李四");
System.out.println(shenClone.getValue());
//[张三]
}
}
プライベート クラス変数の独立したコピーを作成します。
この方法は、2つのオブジェクト間に関連性がなく、相互に影響を与えない完全なコピーを実現するものであり、これがディープコピーである。