原型模式&clone的学习

克隆羊问题:

 有一只名叫Tom,年龄为1,毛为白色的羊。请编写程序创建和Tom羊属性完全一致的10只羊。
 

传统方法:

在这里插入图片描述
代码:

public class Sheep {
    private String name;
    private int age;
    private String color;

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

    //get() && set()
}
public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("Tom", 1, "白色");

        Sheep sheep1 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
        Sheep sheep2 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
        Sheep sheep3 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
        //...
    }
}

 

缺点:

 在创建新的对象时,总是需要重新获得原始对象的属性,如果创建的对象比较复杂时,效率较低。

 总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活。

改进的思路:

 Java的最大类Object,提供一个克隆方法clone()。实现clone方法需要实现一个接口Cloneable,该接口表示实现类能够复制并且具有复制的能力。=》【原型模式】

 

原型模式

 用原型实例指定创建对象的种类,并通过拷贝这些原型,创建新的对象。

 允许一个对象再创建另外一个可定制对象,而无需知道创建的细节。

 工作原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()
 

原型模式结构

在这里插入图片描述 1.原型 (Prototype) 接口将对克隆方法进行声明。 在绝大多数情况下, 其中只会有一个名为 clone克隆的方法。
 2.具体原型 (Concrete Prototype) 类将实现克隆方法。 除了将原始对象的数据复制到克隆体中之外, 该方法有时还需处理克隆过程中的极端情况, 例如克隆关联对象和梳理递归依赖等等。
 3.客户端 (Client) 可以复制实现了原型接口的任何对象。

 

原型模式适合应用场景

 1.如果你需要复制一些对象, 同时又希望代码独立于这些对象所属的具体类, 可以使用原型模式。
 2.如果子类的区别仅在于其对象的初始化方式, 那么你可以使用该模式来减少子类的数量。 别人创建这些子类的目的可能是为了创建特定类型的对象。

 

实现方式

 1.创建原型接口, 并在其中声明 克隆方法。 如果你已有类层次结构, 则只需在其所有类中添加该方法即可。

 2.原型类必须另行定义一个以该类对象为参数的构造函数。 构造函数必须复制参数对象中的所有成员变量值到新建实体中。 如果你需要修改子类, 则必须调用父类构造函数, 让父类复制其私有成员变量值。

 如果编程语言不支持方法重载, 那么你可能需要定义一个特殊方法来复制对象数据。 在构造函数中进行此类处理比较方便, 因为它在调用 new运算符后会马上返回结果对象。

 3.克隆方法通常只有一行代码: 使用 new运算符调用原型版本的构造函数。 注意, 每个类都必须显式重写克隆方法并使用自身类名调用 new运算符。 否则, 克隆方法可能会生成父类的对象。

 4.你还可以创建一个中心化原型注册表, 用于存储常用原型。

 你可以新建一个工厂类来实现注册表, 或者在原型基类中添加一个获取原型的静态方法。 该方法必须能够根据客户端代码设定的条件进行搜索。 搜索条件可以是简单的字符串, 或者是一组复杂的搜索参数。 找到合适的原型后, 注册表应对原型进行克隆, 并将复制生成的对象返回给客户端。

 最后还要将对子类构造函数的直接调用替换为对原型注册表工厂方法的调用。
 

原型模式优缺点

 优点:
  ✔️你可以克隆对象, 而无需与它们所属的具体类相耦合。
  ✔️你可以克隆预生成原型, 避免反复运行初始化代码。
  ✔️你可以更方便地生成复杂对象。
  ✔️你可以用继承以外的方式来处理复杂对象的不同配置。
 
 缺点:
  ❌克隆包含循环引用的复杂对象可能会非常麻烦

更改后的克隆羊项目

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


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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
    
    protected Object clone(){
        Sheep sheep = null;

        try {
            sheep = (Sheep) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        return sheep;
    }
}

public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("Tom", 1, "白色");

        Sheep sheep1 = (Sheep) sheep.clone();
        Sheep sheep2 = (Sheep) sheep.clone();
        Sheep sheep3 = (Sheep) sheep.clone();
        //...
    }
}

 

探讨浅拷贝和深拷贝问题

 

 浅拷贝

​   对于基本类型和String类型的成员变量,浅拷贝会直接进行值传递。

​   对于引用数据类型的成员变量,浅拷贝会进行引用传递(传地址)。则两个对象都会指向同一个实例。在这种情况下,一个对象修改该成员变量会对另一个对象产生影响。

​   实现方法:super.clone()默认的clone方法

 

代码的问题展示

​ 拷贝对象

public class CloneAbleTarget implements Cloneable {
    public String name;
    public int age;
    public CloneAbleTarget cloneAbleTarget;

    public CloneAbleTarget(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public CloneAbleTarget(String name, int age, CloneAbleTarget cloneAbleTarget) {
        this.name = name;
        this.age = age;
        this.cloneAbleTarget = cloneAbleTarget;
    }

    @Override
    public String toString() {
        return "CloneAbleTarget{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", cloneAbleTarget=" + cloneAbleTarget +
                '}';
    }

    @Override
    protected Object clone()  {
        CloneAbleTarget cloneAbleTarget = null;

        try {
            cloneAbleTarget = (CloneAbleTarget) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return cloneAbleTarget;
    }
}

test

public class Client {
    public static void main(String[] args) {
        CloneAbleTarget c1 = new CloneAbleTarget("C1",22,new CloneAbleTarget("C1_1",12));

        CloneAbleTarget c2 = (CloneAbleTarget) c1.clone();

        System.out.println(c1);//{name='C1', age=22, cloneAbleTarget=CloneAbleTarget{name='C1_1', age=12, cloneAbleTarget=null}}
        System.out.println(c2);//{name='C1', age=22, cloneAbleTarget=CloneAbleTarget{name='C1_1', age=12, cloneAbleTarget=null}}

        //此时修改c2的相关属性
        c2.name="C2";
        c2.age = 11;
        c2.cloneAbleTarget.name="C2_2";
        c2.cloneAbleTarget.age=13;

        //进行输出 查看哪些成员变量变了
        System.out.println(c1); //{name='C1', age=22, cloneAbleTarget=CloneAbleTarget{name='C2_2', age=13, cloneAbleTarget=null}}
        System.out.println(c2); //{name='C2', age=11, cloneAbleTarget=CloneAbleTarget{name='C2_2', age=13, cloneAbleTarget=null}}

        /*
            从结果对比可知 c2修改了引用类型,c1的引用类型也随之变化。【地址传递】
         */
    }
}

 

 深拷贝

  ​ 拷贝对象的所有基本类型,并为所有的引用类型变量申请存储空间,并复制每个成员变量所引用的对象。
 

实现方式

1.重写clone方法来实现深拷贝

​  引用类型类

public class DeepCloneabeTarget implements Cloneable {
    private String cloneName;
    private String cloneClass;

    public DeepCloneabeTarget(String cloneName, String cloneClass) {
        this.cloneName = cloneName;
        this.cloneClass = cloneClass;
    }

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

​ 复制类

public class DeepProtoType implements Cloneable {
    private String name;
    private DeepCloneabeTarget deepCloneabeTarget;

    public DeepProtoType(String name, DeepCloneabeTarget deepCloneabeTarget) {
        this.name = name;
        this.deepCloneabeTarget = deepCloneabeTarget;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public DeepCloneabeTarget getDeepCloneabeTarget() {
        return deepCloneabeTarget;
    }

    public void setDeepCloneabeTarget(DeepCloneabeTarget deepCloneabeTarget) {
        this.deepCloneabeTarget = deepCloneabeTarget;
    }

    @Override
    public String toString() {
        return "DeepProtoType{" +
                "name='" + name + '\'' +
                ", deepCloneabeTarget=" + deepCloneabeTarget +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object deep = null;
        //复制基本类型+String类型
        deep = super.clone();
        //对引用类型,单独处理
        DeepProtoType deepProtoType = (DeepProtoType) deep;
        deepProtoType.deepCloneabeTarget = (DeepCloneabeTarget) deepCloneabeTarget.clone();

        return deepProtoType;
    }
}

test

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        DeepProtoType deep1 = new DeepProtoType("Tom",new DeepCloneabeTarget("Name_clone","Class_clone"));

        DeepProtoType deep2 = (DeepProtoType) deep1.clone();

        System.out.println(deep1);//{name='Tom', deepCloneabeTarget=com.rz.prototype.DeepCloneabeTarget@3d075dc0}
        System.out.println(deep2);//{name='Tom', deepCloneabeTarget=com.rz.prototype.DeepCloneabeTarget@214c265e}
        /*
            从结果上看 对象和复制对象的引用类型成员变量的地址不同。则深拷贝成功
         */
    }
}

 

2.通过对象序列化实现深拷贝

​  引用类型类

public class DeepCloneabeTarget implements Serializable{
    private String cloneName;
    private String cloneClass;

    public DeepCloneabeTarget(String cloneName, String cloneClass) {
        this.cloneName = cloneName;
        this.cloneClass = cloneClass;
    }

    @Override
    public String toString() {
        return "DeepCloneabeTarget{" +
                "cloneName='" + cloneName + '\'' +
                ", cloneClass='" + cloneClass + '\'' +
                '}';
    }
}

​ 复制类

public class DeepProtoType implements Serializable,Cloneable {
    private String name;
    private DeepCloneabeTarget deepCloneabeTarget;

    public DeepProtoType(String name, DeepCloneabeTarget deepCloneabeTarget) {
        this.name = name;
        this.deepCloneabeTarget = deepCloneabeTarget;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public DeepCloneabeTarget getDeepCloneabeTarget() {
        return deepCloneabeTarget;
    }

    public void setDeepCloneabeTarget(DeepCloneabeTarget deepCloneabeTarget) {
        this.deepCloneabeTarget = deepCloneabeTarget;
    }

    @Override
    public String toString() {
        return "DeepProtoType{" +
                "name='" + name + '\'' +
                ", deepCloneabeTarget=" + deepCloneabeTarget +
                '}';
    }

    @Override
    protected Object clone(){
        //创建流对象
        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);
            DeepProtoType copyObj = (DeepProtoType) ois.readObject();

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

test

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        DeepProtoType deep1 = new DeepProtoType("Tom",new DeepCloneabeTarget("Name_clone","Class_clone"));

        DeepProtoType deep2 = (DeepProtoType) deep1.clone();

        System.out.println(deep1+" "+deep1.getDeepCloneabeTarget().hashCode());
        System.out.println(deep2+" "+deep2.getDeepCloneabeTarget().hashCode());
        /*
            DeepProtoType{name='Tom', deepCloneabeTarget=DeepCloneabeTarget{cloneName='Name_clone', cloneClass='Class_clone'}} 1650967483
            DeepProtoType{name='Tom', deepCloneabeTarget=DeepCloneabeTarget{cloneName='Name_clone', cloneClass='Class_clone'}} 1604839423
            从结果上看 对象和复制对象的引用类型成员变量的地址不同。则深拷贝成功
         */
    }
}

 

原型模式的一些细节

​  1.创建新对象较为复杂时,可以利用原型模式简化创建过程,提升效率

​  2.不用重新初始化对象,而是动态地获取对象运行时的状态

​  3.如果原始对象发生变化【增加或较少属性】,则克隆对象也会发生改变

​  4.在实现深拷贝时推荐反序列化的方法

需要给每个类都配置克隆方法,则会对已有的类进行改造,违背了OCP原则。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值