23のデザインモード-プロトタイプモード[クローン羊、浅いコピー、深いコピー]

一連の記事

23のデザインパターン-デザインパターンの目的と
23のデザインパターンに従う7つの原則-シングルトンモード[空腹、怠惰、ダブルチェック、静的内部クラス、列挙]
23のデザインパターン-ファクトリモード[シンプルファクトリ、ファクトリメソッド、抽象ファクトリ]
23のデザインモード-プロトタイプモード[クローンシープ、浅いコピー、深いコピー]


3.プロトタイプモード

3.1。伝統的な方法で羊のクローンを作成する

現在、トムという名前の2歳の白い羊がいます。このプログラムを使用して、トムとまったく同じ属性を持つ10頭の羊を作成します。

元のアイデア:直接新しい10回

ここに画像の説明を挿入します

package design_partten.prototype.type1;

public class Sheep {
    
    
    private String name;
    private int age;
    private String color;
    //省略构造方法、set、get
}

public class Client {
    
    
    public static void main(String[] args) {
    
    
        Sheep sheep1 = new Sheep("Tom", 3, "白色");
        Sheep sheep2 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());
        Sheep sheep3 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());
        Sheep sheep4 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());
        Sheep sheep5 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());
        //...
    }
}

長所と短所:

  • 理解が深まり、シンプルで操作が簡単

  • 新しいオブジェクトを作成するときは、常に元のオブジェクトのプロパティを再取得する必要があります。作成されたオブジェクトがより複雑な場合、効率は非常に低くなります。

  • オブジェクトは常に再初期化する必要があり、オブジェクトの実行時状態を動的に取得することはできません(たとえば、新しい属性が元のクラスに追加された場合、新しいクラスのコードを変更する必要があります)。これは十分な柔軟性がありません。 。

  • 改善のアイデア:

    JavaのObjectクラスは、すべてのクラスのルートクラスであり、Javaオブジェクトのコピーを作成できるclone()メソッドを提供します。Cloneableインターフェースを実装する必要があります。これは、このクラスをコピーでき、コピーする機能があることを意味します。これがプロトタイプモデルにつながります。

 

3.2、プロトタイプモードで羊のクローンを作成する

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

  • 仕組み:プロトタイプオブジェクトを作成するオブジェクトに渡します。作成するオブジェクトは、作成を実装するために自分自身をコピーするように要求します。つまり、object.clone()

ここに画像の説明を挿入します

public class Sheep implements Cloneable {
    
    
    private String name;
    private int age;
    private String color;
    private String address="内蒙古";
	//省略构造方法、set、get、toString
    @Override
    protected Object clone() throws CloneNotSupportedException {
    
    
        Sheep sheep = null;
        try {
    
    
            sheep = (Sheep)super.clone();
        }catch (Exception e){
    
    
            e.getStackTrace();
        }
        return sheep;
    }
}

テスト

public class Client {
    
    
    public static void main(String[] args) {
    
    
        Sheep sheep1 = new Sheep("Tom", 3, "白色");
        Sheep sheep2 = (Sheep)sheep1.clone();
        Sheep sheep3 = (Sheep)sheep1.clone();
        //...
        System.out.println(sheep2);
    }
}

結果:

ここに画像の説明を挿入します

結論:プロトタイプモードで羊のクローンを作成することは、従来の方法よりも柔軟性があり、属性の値を直接クローンします。

 

3.3。Springのプロトタイプパターンのソースコード分析

依存性注入では、プロトタイプモードを選択できます。

ここに画像の説明を挿入します

debugと入力して、Beanを取得する方法を確認してください。

ここに画像の説明を挿入します

ここに豆工場がありますので、ぜひご覧ください。

ここに画像の説明を挿入します

最も重要なdoGetBeanもあります。doGetBeanに入ると、内部で設定したプロトタイプモードが表示されます。

ここに画像の説明を挿入します

彼はそれがシングルトンであるかどうか、それがプロトタイプであるかどうかを判断します

ここに画像の説明を挿入します

 

3.4、ディープコピー

クローン羊のメンバー変数はすべて基本タイプです。メンバーにオブジェクトがある場合はどうなりますか?

メンバー変数がオブジェクトの場合、以前の方法でコピーされた場合、新しいメンバーオブジェクトは作成されません。たとえば、次のようになります。

public class Sheep implements Cloneable {
    
    
    private String name;
    private int age;
    private String color;
    private String address="内蒙古";
    private Sheep friend;
    //....
}

public class Client {
    
    
    public static void main(String[] args) {
    
    
        Sheep sheep1 = new Sheep("Tom", 3, "白色");
        sheep1.setFriend(new Sheep("Bob", 2, "黑色"));
        Sheep sheep2 = (Sheep) sheep1.clone();
        Sheep sheep3 = (Sheep) sheep1.clone();
        //...
        System.out.println(sheep1.getFriend().hashCode());
        System.out.println(sheep2.getFriend().hashCode());
        System.out.println(sheep3.getFriend().hashCode());
    }
}

ここに画像の説明を挿入します

3匹の羊の友達はすべて同じオブジェクトであり、それらの参照アドレスは同じです。これはシャローコピーと呼ばれます(clone()のデフォルトの使用はシャローコピーです)。この場合、1つのクローンの友達を変更すると他のクローンに影響します。クローン。。対照的に、コピーすると、そのオブジェクトタイプのメンバーもコピーされます。これはディープコピーと呼ばれます。

ディープコピーの基本的な紹介:

  • オブジェクトのすべての基本データ型のメンバー変数値をコピーします
  • 参照データ型のすべてのメンバー変数のストレージスペースを申請し、参照データ型の各メンバー変数によって参照されるオブジェクトをコピーします。つまり、オブジェクトのディープコピーは、すべてのメンバー変数をコピーします。
  • ディープコピーの実装:1。Re-clone()メソッド; 2。オブジェクトのシリアル化を通じて実現します。

メソッド1:Re-clone()メソッド(私は怠惰すぎてセットを記述してここに到達できません。変数をパブリックとして宣言するだけです)

package design_partten.prototype.type1.improve;

class Bird implements Cloneable{
    
    
    public String name;

    public Bird(String name) {
    
    
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
    
    
        return super.clone();
    }
}

public class Sheep implements Cloneable {
    
    
    public String name;
    public int age;
    public String color;
    public Bird friend;

    public Sheep(String name, int age, String color, Bird friend) {
    
    
        this.name = name;
        this.age = age;
        this.color = color;
        this.friend = friend;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
    
    

        //1. 先拷贝基本数据类型的成员变量
        Sheep sheep = (Sheep) super.clone();

        //2. 拷贝引用类型的成员变量(即对引用类型成员再进行一次拷贝,然后赋值给刚刚拷贝的克隆羊)
        Bird friend = (Bird) this.friend.clone();
        sheep.friend = friend;

        return sheep;
    }
}

テスト:

public class Client {
    
    
    public static void main(String[] args) throws CloneNotSupportedException {
    
    
        Sheep sheep1 = new Sheep("Tom", 3, "白色", new Bird("鸟"));
        Sheep sheep2 = (Sheep) sheep1.clone();
        Sheep sheep3 = (Sheep) sheep1.clone();
        //...
        System.out.println(sheep1.friend.hashCode());
        System.out.println(sheep2.friend.hashCode());
        System.out.println(sheep3.friend.hashCode());
    }
}

結果:

ここに画像の説明を挿入します

評価:ディープコピーはre-clone()メソッドによって実現されます。見た目はシンプルでコードが少ないですが、参照型のメンバー変数が1つしかないためです。多くのメンバー変数が参照型の場合、各変数は書く必要があります。推奨されません。

方法2:オブジェクトのシリアル化を通じて実現する(推奨)

//在Sheep类添加该方法,且Sheep和Bird实现Serialized接口
public Object deepClone(){
    
    
    //创建字节数组输出流对象
    ByteArrayOutputStream bos = null;
    ObjectOutputStream oos = null;
    ByteArrayInputStream bis = null;
    ObjectInputStream ois = null;

    try {
    
    
        //序列化
        bos = new ByteArrayOutputStream();
        oos = new ObjectOutputStream(bos);
        oos.writeObject(this);//当前这个对象以对象流的方式输出

        //反序列化
        bis = new ByteArrayInputStream(bos.toByteArray());
        ois = new ObjectInputStream(bis);
        Sheep sheep = (Sheep)ois.readObject();

        return sheep;
    }catch (Exception e){
    
    
        e.getStackTrace();
        return null;
    }finally {
    
    
        //关闭流
        try {
    
    
            ois.close();
            bis.close();
            oos.close();
            bos.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

 

3.5まとめ

  • 新しいオブジェクトの作成がより複雑な場合は、プロトタイプモードを使用して、オブジェクトの作成プロセスを簡素化すると同時に、効率を向上させることができます。
  • オブジェクトを再初期化する必要はありませんが、オブジェクトの実行時状態を動的に取得します。つまり、オブジェクトのプロパティが後で変更された場合、プロパティ変更後のオブジェクトは、元のオブジェクトではなく、クローン作成によって取得されます。
  • 元のオブジェクトが変更された場合(属性の増加または減少)、コードを変更せずに、他の複製されたオブジェクトもそれに応じて変更されます。
  • 短所:クローンメソッドを増やす必要があります。これは、新しいクラスでは問題ありませんが、以前に作成されたクラスでは、OCPの原則に違反します。

おすすめ

転載: blog.csdn.net/qq_39763246/article/details/114582595