设计模式详解--原型模式

设计模式详解--原型模式

是指原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。而调用者不需要知道任何创建细节,不调用构造函数

其实就是如何快速构建对象的方法总结,比如简单工厂将getter setter封装到某个方法中

JDK提供实现Cloneable接口,实现快速复制。当初在spring配置scope="prototype",、scope="singotn"就是在设置是否原型模式创建对象

属于创建型模式

适用场景:

1)类初始化消耗资源较多

2)new产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)

3)构造函数比较复杂

4)循环体中生产大量对象时

克隆分为深克隆和浅克隆

⑴浅复制(浅克隆) 

    被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。 Object类提供的方法clone只是拷贝本对象,就是在实体类中实现cloneable接口然后重写clone()方法即可,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址

我们这里手写一个接口模拟jdk原生的拷贝

先写一个代替clonable的接口Prototype

1.

public interface Prototype{
    Prototype clone();
}

2.然后再写一个实体类来实现这个接口并重写clone方法

public class ConcretePrototypeA 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 ConcretePrototypeA clone() {
        ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
        concretePrototype.setAge(this.age);
        concretePrototype.setName(this.name);
        concretePrototype.setHobbies(this.hobbies);
        return concretePrototype;
    }

}

可以看到我们这里clone方法就是new一个对象然后把已有的值赋给它。

3.写一个客户端来代理原型模式(也可以不写直接在测试类中调用)

public class Client {

    private Prototype prototype;

    public Client(Prototype prototype){
        this.prototype = prototype;
    }
    public Prototype startClone(Prototype concretePrototype){
        return (Prototype)concretePrototype.clone();
    }

}

4.写一个测试类看看两个对象中的某个元素的地址是否一致

public class PrototypeTest {

    public static void main(String[] args) {

        // 创建一个具体的需要克隆的对象
        ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
        // 填充属性,方便测试
        concretePrototype.setAge(18);
        concretePrototype.setName("prototype");
        List hobbies = new ArrayList<String>();
        concretePrototype.setHobbies(hobbies);
        System.out.println(concretePrototype);

        // 创建Client对象,准备开始克隆
        Client client = new Client(concretePrototype);
        ConcretePrototypeA concretePrototypeClone = (ConcretePrototypeA) client.startClone(concretePrototype);
        System.out.println(concretePrototypeClone);

        System.out.println("克隆对象中的引用类型地址值:" + concretePrototypeClone.getHobbies());
        System.out.println("原对象中的引用类型地址值:" + concretePrototype.getHobbies());
        System.out.println("对象地址比较:"+(concretePrototypeClone.getName() == concretePrototype.getName()));


    }
}

 

6.输出结果截图

可以看到name的属性复制是浅克隆,地址是一致的

⑵深复制(深克隆) 

      被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。 

我们以孙悟空拔下三根救命毫毛可以变出多个猴子猴孙为例

1.首先写一个猴子的实体父类

public class Monkey {
    public int height;
    public int weight;
    public Date birthday;

}

2.然后再写一个金箍棒的实体,因为千变万化的孙猴子不可能用一个金箍棒吧,所以这里要实现深克隆,实现serializable接口

public class JinGuBang implements Serializable {
    public float h = 100;
    public float d = 10;

    public void big(){
        this.d *= 2;
        this.h *= 2;
    }

    public void small(){
        this.d /= 2;
        this.h /= 2;
    }
}

3.写一个齐天大圣的实体

public class QiTianDaSheng extends Monkey implements Cloneable,Serializable {

    public JinGuBang jinGuBang;

    public  QiTianDaSheng(){
        //只是初始化
        this.birthday = new Date();
        this.jinGuBang = new JinGuBang();
    }

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


    public 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);

            QiTianDaSheng copy = (QiTianDaSheng)ois.readObject();
            copy.birthday = new Date();
            return copy;

        }catch (Exception e){
            e.printStackTrace();
            return null;
        }

    }


    public QiTianDaSheng shallowClone(QiTianDaSheng target){

        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
        qiTianDaSheng.height = target.height;
        qiTianDaSheng.weight = target.height;

        qiTianDaSheng.jinGuBang = target.jinGuBang;
        qiTianDaSheng.birthday = new Date();
        return  qiTianDaSheng;
    }


}

这个类要同时实现clonable和serializable接口,我在这个类中写了两个方法一个是深克隆一个是浅克隆。

4.编写测试类

public class DeepCloneTest {

    public static void main(String[] args) {

        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
        try {
            QiTianDaSheng clone = (QiTianDaSheng)qiTianDaSheng.clone();
            System.out.println("深克隆:" + (qiTianDaSheng.jinGuBang == clone.jinGuBang));
        } catch (Exception e) {
            e.printStackTrace();
        }

        QiTianDaSheng q = new QiTianDaSheng();
        QiTianDaSheng n = q.shallowClone(q);
        System.out.println("浅克隆:" + (q.jinGuBang == n.jinGuBang));


    }
}

5.运行结果

可以发现深克隆的结果是产生了不同的对象,深克隆成功

(3)总结

深克隆可能会产生单例被破坏的情况,与单例唱反调。所以如果不想让单例被破坏可以不实现cloneable

缺点:

必须配备克隆(或者可拷贝)方法

对克隆复杂对象或对克隆出的对象进行复杂改造时,容易带来风险

深拷贝、浅拷贝要运用得当

原型模式的介绍就到这里啦,如有不当之处,烦请流言指正。下一篇,设计模式详解--代理模式

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章