设计模式之原型模式

原型模式引入

​ 在我们实际开发过程中,经常会遇到有些实体类中的属性特别多,我们需要一个一个的去匹配对应的属性然后set进去。这样我们也忍了,但是可恨的是,有时候我们需要创建这个类很多次,基本上复制过去的,但是还不能用原来的对像,就像spring中有一个属性scope="prototype"设置的操作,还有JSON.parseObject(),就必须要把值给弄过去。这些操作复制对应值的时候肯定不是先get后set了,那得多麻烦,还消耗资源。这就引入了我们的原型模式。

原型模式是指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

适用场景如下:

  • 类初始化小号资源较多
  • 使用new生成一个对象需要非常繁琐的过程
  • 构造函数复杂
  • 在循环体中产生大量的对象

使用原型模式的主要有两种:浅克隆和深克隆

浅克隆

​ 浅克隆,顾名思义,就是表面的复制,并没有影响人家的内涵。浅克隆复制的是引用的地址而不是值。也就是如果我们修改了原型的其中一个属性的值,那么克隆出来的值也会发生变化。我们看如下代码:

public interface Prototype {
    Prototype clone();
}

public class ClonePrototype implements Prototype {

    private int age;
    private String name;
    private List 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 List getHobbies() {
        return hobbies;
    }

    public void setHobbies(List hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public ClonePrototype clone() {
        ClonePrototype clone = new ClonePrototype();
        clone.setAge(this.age);
        clone.setName(this.name);
        clone.setHobbies(this.hobbies);
        return clone;
    }
}

public class Client {

    public Prototype startClone(Prototype concretePrototype) {
        return concretePrototype.clone();
    }
}

public class PrototypeTest {
    public static void main(String[] args) {
        // 原型,一个被克隆的对象,也就是母体
        ClonePrototype clonePrototype = new ClonePrototype();
        clonePrototype.setAge(18);
        clonePrototype.setName("Naomi");
        List hobbies = new ArrayList<>();
        hobbies.add("shilylyp");
        clonePrototype.setHobbies(hobbies);
        System.out.println("母体:" + clonePrototype);

        // 创建复制的工具,开始复制原型
        Client client = new Client();
        ClonePrototype clone = (ClonePrototype)client.startClone(clonePrototype);
        System.out.println("复制体:" + clone);

        System.out.println("母体中属性的引用地址:" + clonePrototype.getHobbies());
        System.out.println("复制体中属性的引用地址:" + clone.getHobbies());
        System.out.println("两者是否相等:" + (clone.getHobbies() == clonePrototype.getHobbies()));
    }
}

测试结果如下:

母体:com.shmilylyp.demo10.ClonePrototype@5cad8086
复制体:com.shmilylyp.demo10.ClonePrototype@6e0be858
母体中属性的引用地址:[shilylyp]
复制体中属性的引用地址:[shilylyp]
两者是否相等:true

Process finished with exit code 0

很显然是一样的,这样的结果当然不是我们想要的。有浅就有深么,不能因为你短就说所有人都短是吧。

深克隆

废话不多说,直接上代码,我们要复制好多苹果味的蛋糕。这次吃个饱,嘻嘻。

public class AppleCake implements Cloneable, Serializable {
    // 苹果酱
    public List apples;

    public AppleCake() {
        List appleList = new ArrayList();
        appleList.add("apple");
        this.apples = appleList;
    }

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

    private Object 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);

            AppleCake appleCake = (AppleCake)ois.readObject();
            return appleCake;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    public AppleCake shallowClone(AppleCake cake) {
        AppleCake appleCake = new AppleCake();
        appleCake.apples = cake.apples;
        return appleCake;
    }
}

public class AppleCakeTest {

    public static void main(String[] args) {
        AppleCake appleCake1 = new AppleCake();
        try {
            AppleCake appleCakeDeep = (AppleCake)appleCake1.clone();
            System.out.println("深克隆属性:" + (appleCake1.apples == appleCakeDeep.apples));
            System.out.println("深克隆实体:" + (appleCake1 == appleCakeDeep));
        }catch (Exception e){
            e.printStackTrace();
        }

        AppleCake appleCake2 = new AppleCake();
        AppleCake appleCakeShallow = appleCake2.shallowClone(appleCake2);
        System.out.println("浅克隆属性:" + (appleCake2.apples == appleCakeShallow.apples));
        System.out.println("浅克隆实体:" + (appleCake2 == appleCakeShallow));
    }
}

运行结果如下:

深克隆属性:false
深克隆实体:false
浅克隆属性:true
浅克隆实体:false

Process finished with exit code 0

由此可见虽然深克隆和浅克隆弄出来的实体都是不一样的,但是浅克隆里面的属性是相等的。

克隆破坏单例

从深克隆的结果来看,我们也能够看出克隆是可以破坏掉单例的,浅克隆是没有破坏的,实际内存还是那个,只不过复制了一个引用地址而已,不必惊慌。因此当我们使用单例的时候就需要防止这种情况的发生。其实也很简单,只要我们禁止掉深克隆就OK了,不然你克隆,你还能搞破坏???我们可以这样做:

  • 单例类的时候不实现Cloneable接口
  • 在重写方法clone()中返回原来的实例

你可能感兴趣的:(设计模式之原型模式)