【创建型设计模式】原型模式

允我心安 提交于 2019-12-24 04:22:34

前言(一些废话,可以忽略)

  • 同样是创建型的设计模式,这种模式与工厂模式相比,结构更为简单,也更好理解,直接进入正题
  • PS.部分类实现见文末

要解决的问题

  • 克隆羊多利大家都知道,原型模式要解决的就是对象的复制问题,在没有原型模式的情况下,我们直接通过复制对象的属性值来获取新的相同对象
/**
 * 羊
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class Sheep {
    private String name;
    private Integer age;

    public Sheep(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
	//...toString
	//...setter
	//...getter
}
  • 在调用的时候直接通过获取对象属性,或设置相同的属性值来进行克隆,显然很原始,一旦原始对象发生变化,我们需要修改克隆使用的地方
/**
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class BaseClone {

    public static void main(String[] args) {
        Sheep sheep1 = new Sheep("Sheep1", 1);
        //获取原对象属性
        Sheep sheep2 = new Sheep(sheep1.getName(), sheep1.getAge());
        //设置与原对象属性相同的值
        Sheep sheep3 = new Sheep("Sheep1", 1);
        System.out.println(sheep1);
        System.out.println(sheep2);
        System.out.println(sheep3);

        System.out.println(sheep1.hashCode());
        System.out.println(sheep2.hashCode());
        System.out.println(sheep3.hashCode());
    }
}

浅拷贝原型模式

  • “浅拷贝原型模式”,这个名字是我自己取的,如果雷同是巧合,反正原型模式的主要目的是对象的复制(克隆),与上一篇一样,我们同样不需要在意这些细节
  • 浅拷贝,直接使用Object的clone方法,一个本地的复制方法,快速!高效!
  • 实现一个CopyPrototype接口,自己写一个clone方法的接口方便深拷贝时重写clone方法,也可以直接实现Cloneable方法,然后调用父类clone实现拷贝
/**
 * 可以克隆的羊
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class Sheep implements Cloneable, CopyPrototype{
    private String name;
    private Integer age;

    @Override
    public CopyPrototype copyOnSelf() throws CloneNotSupportedException {
        return (CopyPrototype) this.clone();
    }
    
    //@Override
    //protected Object clone() throws CloneNotSupportedException {
    //	return super.clone();
	//
    //}
}
  • 克隆方法的时候,直接调用接口的复制自我方法copyOnSelf
/**
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class ShallowPrototype {

    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheep1 = new Sheep("sheep1", 2);
        Sheep sheep2 = (Sheep) sheep1.copyOnSelf();
		//值一样
        System.out.println(sheep1);
        System.out.println(sheep2);
		//false,说明对象已经被复制
        System.out.println(sheep1.hashCode() == sheep2.hashCode());
		
    }
}

使用clone的深拷贝

  • 存在一个问题,当我们的sheep还有一个引用对象作为成员变量的时候,如下

/**
 * 羊
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class Sheep implements Cloneable, CopyPrototype {
    private String name;
    private Integer age;

    private SheepProperties properties;

    @Override
    public CopyPrototype copyOnSelf() throws CloneNotSupportedException {
        Sheep sheep =  (Sheep) this.clone();
        
        return sheep;
    }
}

public static void main(String[] args) throws CloneNotSupportedException {
    Sheep sheep1 = new Sheep("sheep1", 2);
    SheepProperties properties = new SheepProperties("中国", "山羊");
    sheep1.setProperties(properties);
    Sheep sheep2 = (Sheep) sheep1.copyOnSelf();

    System.out.println(sheep1);
    System.out.println(sheep2);

    System.out.println(sheep1.hashCode());
    System.out.println(sheep2.hashCode());

    System.out.println(sheep1.getProperties().hashCode());
    System.out.println(sheep2.getProperties().hashCode());
}
  • 这时我们会发现,sheep1和sheep2中的属性properties的hashCode(如果没有重写,则是经由内存地址计算得出)会是一样的,这说明我们在克隆的时候没有复制引用对象,这就是一个问题,所以如果有引用对象,则需要将引用对象也实现Cloneable接口(我们自己的接口CopyPrototype可以不实现,与之前的逻辑一样)
    @Override
    public CopyPrototype copyOnSelf() throws CloneNotSupportedException {
        Sheep sheep =  (Sheep) this.clone();
       	//深拷贝
		sheep.setProperties((SheepProperties) properties.copyOnSelf());
        return sheep;
    }
class SheepProperties implements Cloneable, CopyPrototype {
    private String placeOfProduction;
    private String breeding;

    @Override
    public CopyPrototype copyOnSelf() throws CloneNotSupportedException {
        return (CopyPrototype) this.clone();
    }
}
  • 存在一个问题,如果我们需要再在羊Sheep类中增加新的属性呢,那么就需要在重写copyOnSelf方法,这就违反了ocp原则,如果我们的SheepProperties类中也要增加新的引用对象呢,又得重写,这样就无穷尽了,于是乎

基于序列化的深拷贝

  • 我们使用流的方式将对象序列化,这样即使再对类做修改,或引用对象中还有引用对象,都无需去修改copyOnSelf方法,这样扩展起来就比较好了
/**
 * 羊
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class Sheep implements Cloneable, CopyPrototype, Serializable {
    private String name;
    private Integer age;

    private SheepProperties properties;
	//...省略一些方法
    @Override
    public CopyPrototype copyOnSelf() throws Exception{

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
        objectOutputStream.writeObject(this);

        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(in);
        Sheep sheep = (Sheep) objectInputStream.readObject();
        return sheep;
    }
}
  • 属性SheepProperties 只需要实现序列化接口即可,如果属性中还有引用对象,当然也都需要实现序列化接口,就不需要实现copyOnSelf方法
class SheepProperties implements Serializable {
    private String placeOfProduction;
    private String breeding;

    public SheepProperties(String placeOfProduction, String breeding) {
        this.placeOfProduction = placeOfProduction;
        this.breeding = breeding;
    }
	//...省略一些常规方法
}
  • 最后还是通过测试方法,看一下克隆出来的对象的引用属性是否为同一个
/**
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class DeepPrototype {

    public static void main(String[] args) throws Exception {
        Sheep sheep1 = new Sheep("sheep1", 2);
        SheepProperties properties = new SheepProperties("中国", "山羊");
        sheep1.setProperties(properties);
        Sheep sheep2 = (Sheep) sheep1.copyOnSelf();

        System.out.println(sheep1);
        System.out.println(sheep2);

        System.out.println(sheep1.hashCode());
        System.out.println(sheep2.hashCode());

        System.out.println(sheep1.getProperties().hashCode());
        System.out.println(sheep2.getProperties().hashCode());
    }
}

在这里插入图片描述

  • 属性的hashcode不是同一个,证实引用类型也被克隆了,但是我们在SheepProperties中并没有去写序列化相关代码,扩展性提高了,这是推荐使用的深拷贝,也是所谓的原型模式

总结

  • 原型模式简单来说就是对象的复制,看了一些其他同学关于原型模式的写法,和我的不完全一样,还是那句话形不似,但神似,精神一脉相承,足矣!当然如果你有任何问题,欢迎私信我,我们一起讨论,嘻嘻😳

愿你不舍爱与自由。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!