设计模式04-原型模式

学习目标:

  》原型模式的应用场景

  》原型模式之深克隆与浅克隆

  》了解克隆是如何破坏单例的

  》原型模式的优缺点

  》掌握建造者模式和工厂模式的区别

知识前提:

  》了解并掌握工厂模式

  》已掌握单例模式

  》听说过模式,但想要深入学习

1.1.原型模式的定义

  Prototype Pattern,是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

  调用者不需要知道任何创建细节,不调用构造函数。

  属于创建型模式

 

原型模式其实在开发中很常见。比如:我们对一个对象设值时,通常的做法:

先new 一个实例,然后调用set方法,传参另一个对象【原型】,调用get方法进行设值,如下所示代码:

package com.wf.prototype;

import lombok.Data;

/**
 * @ClassName ExamPaper
 * @Description 考试试卷
 * @Author wf
 * @Date 2020/5/7 12:49
 * @Version 1.0
 */
@Data
public class ExamPaper {
    private Long id;
    private String name;

    public ExamPaper copy (){
        ExamPaper examPaper = new ExamPaper();
        examPaper.setId(this.getId());
        examPaper.setName(this.getName());
        //....
        return examPaper;
    }
}

注意:

  这里使用lombok组件,依赖如下:

<dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.10</version>
      </dependency>

这里copy方法,就用到的原型模式。【原来的对象,生成另一个对象】

但是,它有一些问题:

  》代码使用大量get,set转换。重复度很高

  》代码不够优雅

  》其实是一种硬编码形式,当增加一个字段时,就必须得修改copy方法。

 

为了改善上面的问题,基于反射,提取bean转换工具类:

1.1.1.beanUtil工具类

 

package com.wf.prototype;

import java.lang.reflect.Field;

/**
 * @ClassName BeanUtils
 * @Description 对象转换工具类
 * @Author wf
 * @Date 2020/5/7 12:51
 * @Version 1.0
 */
public class BeanUtils {
    public static Object copy(Object protoType){
        Class<?> clazz = protoType.getClass();
        Object returnValue = null;
        try {
            returnValue = clazz.newInstance();
            for (Field field: clazz.getDeclaredFields()){
                field.setAccessible(true);
                //根据原型.get,设置给set
                //通过反射调用,returnValue.setXXx(proto.getXXx())
                field.set(returnValue,field.get(protoType));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    return returnValue;
    }
}

说明:

  上面的工具类,使用了反射进行设值,就是一种原型模式的处理。它的本质和set设值是一样的,不过,代码更为优雅。

1.2.原型模式的代码示例

1.2.1.使用原始的硬编码式实现原型对象创建

1.2.1.1.顶层接口

package com.wf.prototype.general;

/**
 * @ClassName IProtoType
 * @Description 原型实例的顶层接口
 * @Author wf
 * @Date 2020/5/12 13:59
 * @Version 1.0
 */
public interface IProtoType<T> {
    T clone();
}

1.2.1.2.原型接口实现

package com.wf.prototype.general;

/**
 * @ClassName ConcretePrototype
 * @Description 具体的原型类实现
 * @Author wf
 * @Date 2020/5/12 14:00
 * @Version 1.0
 */
public class ConcretePrototype implements IProtoType {
    private int age;
    private String name;

    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;
    }

    @Override
    public ConcretePrototype clone() {
        ConcretePrototype concretePrototype = new ConcretePrototype();
        concretePrototype.setAge(this.age);
        concretePrototype.setName(this.name);
        return concretePrototype;
    }
}

说明:

  实现类中,定义clone方法,采用new的方式,然后硬编码set值。产生新的原型实例。

 

1.2.1.3.客户端代码调用

package com.wf.prototype.general;

/**
 * @ClassName Client
 * @Description TODO
 * @Author wf
 * @Date 2020/5/12 14:09
 * @Version 1.0
 */
public class Client {
    public static void main(String[] args) {
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setName("test");
        prototype.setAge(18);
        System.out.println(prototype);

        ConcretePrototype cloneType = prototype.clone();
        System.out.println(cloneType);

    }
}

运行结果如下:

 

说明:

  这里通过调用clone方法,得到一个新的实例。并且成员的值,和原有对象是一样。

  我们在编码中,经常存在对象转换,如:DO转换DTO,或VO。我们需要把成员的值,也一起设置给新的对象。

  就可以使用原型模式。

 

1.2.2.原型模式的适用场景总结

1.类的初始化消耗资源较多【需要调用很多set方法】

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

3.构造函数比较复杂

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

1.2.3.浅克隆与深克隆

1.2.3.1.浅克隆

  Jdk本身已经有了浅克隆的方法实现。通过实现Cloneable接口来实现:

注意:

  通过查询Cloneable接口的源码,发现内部并没有定义clone方法,而是使用Object中clone方法。

  Object是所有类的父类,所以,clone方法定义在Object中,是一个native方法:

protected native Object clone() throws CloneNotSupportedException;
1.代码示例
package com.wf.prototype.shallow;

/**
 * @ClassName ConcretePrototype
 * @Description 具体的原型类实现,基于jdk的实现
 * @Author wf
 * @Date 2020/5/12 14:00
 * @Version 1.0
 */
public class ConcretePrototype implements Cloneable {
    private int age;
    private String name;

    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;
    }

    @Override
    public ConcretePrototype clone() {
        try {
            //调用jdk中clone()实现
            return (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

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

测试类如下:

package com.wf.prototype.shallow;/**
 * @ClassName Client
 * @Description TODO
 * @Author wf
 * @Date 2020/5/12 14:09
 * @Version 1.0
 */
public class Client {
    public static void main(String[] args) {
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setName("test");
        prototype.setAge(18);
        System.out.println(prototype);

        ConcretePrototype cloneType = prototype.clone();
        System.out.println(cloneType);

    }
}

测试结果如下:

 

2.浅克隆的问题探究

  根据上面的测试结果,似乎和自定义clone接口,没有什么不同。

  问题是什么呢?再来看下面的代码:

package com.wf.prototype.shallow;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName ConcretePrototype
 * @Description 具体的原型类实现,基于jdk的实现
 * @Author wf
 * @Date 2020/5/12 14:00
 * @Version 1.0
 */
public class ConcretePrototype implements Cloneable {
    private int age;
    private String name;
    
    //增加一个引用类型成员
    private List<String> hobbies = new ArrayList<>();

    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<String> getHobbies() {
        return hobbies;
    }

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

    @Override
    public ConcretePrototype clone() {
        try {
            //调用jdk中clone()实现
            return (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

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

测试类如下:

package com.wf.prototype.shallow;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName Client
 * @Description TODO
 * @Author wf
 * @Date 2020/5/12 14:09
 * @Version 1.0
 */
public class Client {
    public static void main(String[] args) {
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setName("test");
        prototype.setAge(18);
        //既爱书法,也爱美术
        List<String> hobbies = new ArrayList<>();
        hobbies.add("书法");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);
        System.out.println(prototype);

        ConcretePrototype cloneType = prototype.clone();
        //还是个技术控
        cloneType.getHobbies().add("技术控");
        System.out.println(cloneType);

    }
}

测试结果如下:

 

似乎是正确的。再修改测试代码:

package com.wf.prototype.shallow;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName Client
 * @Description TODO
 * @Author wf
 * @Date 2020/5/12 14:09
 * @Version 1.0
 */
public class Client {
    public static void main(String[] args) {
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setName("test");
        prototype.setAge(18);
        //既爱书法,也爱美术
        List<String> hobbies = new ArrayList<>();
        hobbies.add("书法");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);
        //System.out.println(prototype);    //调整打印顺序

        ConcretePrototype cloneType = prototype.clone();
        //还是个技术控
        cloneType.getHobbies().add("技术控");

        System.out.println(prototype);
        System.out.println(cloneType);
    }
}

测试结果,大出所料,如下:

 说明:

  根据我们的编程意图,我们只是希望修改克隆对象的成员值,而不能改变,原有对象的属性值。

  但是,现在原有对象属性也被修改了,显然是有问题的。

 

为什么会出现这个问题呢?

  猜测:可能原型对象和克隆对象,两个实现指向同一个实例,即它们的内存地址是一样的。

代码验证如下:

package com.wf.prototype.shallow;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName Client
 * @Description TODO
 * @Author wf
 * @Date 2020/5/12 14:09
 * @Version 1.0
 */
public class Client {
    public static void main(String[] args) {
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setName("test");
        prototype.setAge(18);
        //既爱书法,也爱美术
        List<String> hobbies = new ArrayList<>();
        hobbies.add("书法");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);
        //System.out.println(prototype);    //调整打印顺序

        ConcretePrototype cloneType = prototype.clone();
        //还是个技术控
        cloneType.getHobbies().add("技术控");

        System.out.println("原型对象:"+prototype);
        System.out.println("克隆对象:"+cloneType);
        System.out.println(prototype==cloneType);//预期:返回true
    }
}

测试结果如下:

 

总结:

  显然,上面的猜测是错误的。两个实例是不同的对象,指向不同的内存地址。

  那到底是什么原因呢?再次代码调试:

package com.wf.prototype.shallow;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName Client
 * @Description TODO
 * @Author wf
 * @Date 2020/5/12 14:09
 * @Version 1.0
 */
public class Client {
    public static void main(String[] args) {
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setName("test");
        prototype.setAge(18);
        //既爱书法,也爱美术
        List<String> hobbies = new ArrayList<>();
        hobbies.add("书法");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);
        //System.out.println(prototype);    //调整打印顺序

        ConcretePrototype cloneType = prototype.clone();
        //还是个技术控
        cloneType.getHobbies().add("技术控");

        System.out.println("原型对象:"+prototype);
        System.out.println("克隆对象:"+cloneType);
        System.out.println(prototype==cloneType);//预期:返回true,实际为false

        System.out.println("原型对象的爱好属性:"+prototype.getHobbies());
        System.out.println("克隆对象的爱好属性:"+cloneType.getHobbies());
        System.out.println(prototype.getHobbies() == cloneType.getHobbies());
    }
}

测试结果如下:

 

 

 

总结:

  真相浮出水面。我们发现两个对象中成员hobbies的内存地址是一样

  

  我们发现,hobbies是一个List类型成员,是引用类型。jdk中clone方法,在克隆对象时,只是把原型对象成员的内存地址

进行的复制,而不是复制属性的值

  所以,当修改克隆对象属性值,原型对象的属性值也会被改变。

 

  这显然是不符合代码预期的,是存在代码风险的。我们需要的是克隆出一个新的对象,不会影响或修改原型对象。

 

  那么,怎么解决这个问题呢?提出深克隆的解决方案。

 

浅克隆的问题

  主要是针对原型对象中,存在引用类型成员时,产生的数据安全问题。

1.2.3.2.深克隆

   如何实现深克隆呢?方案是什么?

  通过序列化与反序列化来实现。

1.代码实现

首先,需要实现Serializable接口

package com.wf.prototype.deep;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName ConcretePrototype
 * @Description 具体的原型类实现,基于序列化实现深克隆
 * @Author wf
 * @Date 2020/5/12 14:00
 * @Version 1.0
 */
public class ConcretePrototype implements Cloneable, Serializable {
    private int age;
    private String name;

    //增加一个引用类型成员
    private List<String> hobbies = new ArrayList<>();

    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<String> getHobbies() {
        return hobbies;
    }

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

    /**
     * 基于jdk实现浅克隆
     * @return
     */
    @Override
    public ConcretePrototype clone() {
        try {
            //调用jdk中clone()实现
            return (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 基于序列化与反序列化,实现深克隆
     * @return
     */
    public ConcretePrototype 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);

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

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

测试类如下:

package com.wf.prototype.deep;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName Client
 * @Description TODO
 * @Author wf
 * @Date 2020/5/12 14:09
 * @Version 1.0
 */
public class Client {
    public static void main(String[] args) {
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setName("test");
        prototype.setAge(18);
        //既爱书法,也爱美术
        List<String> hobbies = new ArrayList<>();
        hobbies.add("书法");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);
        //System.out.println(prototype);    //调整打印顺序

        ConcretePrototype cloneType = prototype.deepClone();
        //还是个技术控
        cloneType.getHobbies().add("技术控");

        System.out.println("原型对象:"+prototype);
        System.out.println("克隆对象:"+cloneType);
        System.out.println(prototype==cloneType);//预期:返回true,实际为false

        System.out.println("原型对象的爱好属性:"+prototype.getHobbies());
        System.out.println("克隆对象的爱好属性:"+cloneType.getHobbies());
        System.out.println(prototype.getHobbies() == cloneType.getHobbies());
    }
}

测试结果如下:

 

总结:

  这种方式,有以下缺点:

》性能不好

》占用IO

 2.深克隆实现方案2-----Json转换
CustomerDO customerDO= JSONObject.parseObject(JSONObject.toJSONString(customerDTO),CustomerDO.class);

1.3.原型模式与单例模式

1.3.1.浅克隆破坏单例

  如果一个原型对象,本身就是一个单例类,这时提供clone方法,会破坏单例。

1.3.1.1.示例代码

package com.wf.prototype.singleton;

import lombok.Data;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName ConcretePrototype
 * @Description 具体的原型类实现,基于序列化实现深克隆
 * @Author wf
 * @Date 2020/5/12 14:00
 * @Version 1.0
 */
@Data
public class ConcretePrototype implements Cloneable, Serializable {
    private int age;
    private String name;

    //增加一个引用类型成员
    private List<String> hobbies = new ArrayList<>();

    private static ConcretePrototype instance = new ConcretePrototype();
    private ConcretePrototype(){}

    public static ConcretePrototype getInstance(){
        return instance;
    }


    /**
     * 基于jdk实现浅克隆
     * @return
     */
    @Override
    public ConcretePrototype clone() {
        try {
            //调用jdk中clone()实现
            return (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 基于序列化与反序列化,实现深克隆
     * @return
     */
  /*  public ConcretePrototype 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);

            return (ConcretePrototype) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }*/

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

测试代码如下:

package com.wf.prototype.singleton;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName Client
 * @Description TODO
 * @Author wf
 * @Date 2020/5/12 14:09
 * @Version 1.0
 */
public class Client {
    public static void main(String[] args) {
        ConcretePrototype prototype = ConcretePrototype.getInstance();
        prototype.setName("test");
        prototype.setAge(18);
        //既爱书法,也爱美术
        List<String> hobbies = new ArrayList<>();
        hobbies.add("书法");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);
        //System.out.println(prototype);    //调整打印顺序

        ConcretePrototype cloneType = prototype.clone();
        //还是个技术控
        cloneType.getHobbies().add("技术控");

        System.out.println("原型对象:"+prototype);
        System.out.println("克隆对象:"+cloneType);
        System.out.println(prototype==cloneType);//预期:返回true,实际为false

        System.out.println("原型对象的爱好属性:"+prototype.getHobbies());
        System.out.println("克隆对象的爱好属性:"+cloneType.getHobbies());
        System.out.println(prototype.getHobbies() == cloneType.getHobbies());
    }
}

测试结果如下:

 

说明:

  通过全局访问getInstance方法,获取到原生对象。

  再通过clone方法,得到新的克隆对象。

  显然,得到的是两个不同的实例,单例特性被破坏。

 

这个问题怎么解决呢?

  》明确单例与原型本身就是对立的。如果要保证是单例类,就不能是原型类,不能实现clone方法。

  即不实现Cloneable接口,再调用clone方法就会报错。

 

  》可以实现cloneable接口,但clone方法只能返回一个实例。如下所示:

@Data
public class ConcretePrototype implements Cloneable, Serializable {
    private int age;
    private String name;

    //增加一个引用类型成员
    private List<String> hobbies = new ArrayList<>();

    private static ConcretePrototype instance = new ConcretePrototype();
    private ConcretePrototype(){}

    public static ConcretePrototype getInstance(){
        return instance;
    }


    /**
     * 基于jdk实现浅克隆
     * @return
     */
    @Override
    public ConcretePrototype clone() {
        return instance;
    }
}

但是,这没有意义,因为原型根本没有意义。

所以,单例与原型本身就是对立的。不能同时兼顾。单例和原型,只能设计其一。

1.4.原型模式的源码应用

1.4.1.Jdk源码原型实现

1.4.1.1.ArrayList类

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

ArrayList去实现Cloneable接口,再查看clone方法实现:

public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

1.4.1.2.HashMap类实现Cloneable

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

clone方法实现如下:

@Override
    public Object clone() {
        HashMap<K,V> result;
        try {
            result = (HashMap<K,V>)super.clone();
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
        result.reinitialize();
        result.putMapEntries(this, false);
        return result;
    }

1.4.2.基于ArrayList实现Cloneable,实现深克隆应用

package com.wf.prototype.deep;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName ConcretePrototype
 * @Description 具体的原型类实现,基于序列化实现深克隆
 * @Author wf
 * @Date 2020/5/12 14:00
 * @Version 1.0
 */
public class ConcretePrototype implements Cloneable, Serializable {
    private int age;
    private String name;

    //增加一个引用类型成员
    private List<String> hobbies = new ArrayList<>();

    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<String> getHobbies() {
        return hobbies;
    }

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

    /**
     * 基于jdk实现浅克隆
     * @return
     */
    @Override
    public ConcretePrototype clone() {
        try {
            //调用jdk中clone()实现
            return (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 基于序列化与反序列化,实现深克隆
     * @return
     */
    public ConcretePrototype 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);

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

    /**
     * 基于ArrayList底层实现clone,实现深克隆
     * @return
     */
    public ConcretePrototype deepCloneHobbies() {
        try {

            ConcretePrototype res = (ConcretePrototype) super.clone();
            res.hobbies = (List)((ArrayList)res.hobbies).clone();
            return res;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

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

测试类:

package com.wf.prototype.deep;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName Client
 * @Description TODO
 * @Author wf
 * @Date 2020/5/12 14:09
 * @Version 1.0
 */
public class Client {
    public static void main(String[] args) {
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setName("test");
        prototype.setAge(18);
        //既爱书法,也爱美术
        List<String> hobbies = new ArrayList<>();
        hobbies.add("书法");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);
        //System.out.println(prototype);    //调整打印顺序

        ConcretePrototype cloneType = prototype.deepCloneHobbies();//调用clone方法
        //还是个技术控
        cloneType.getHobbies().add("技术控");

        System.out.println("原型对象:"+prototype);
        System.out.println("克隆对象:"+cloneType);
        System.out.println(prototype==cloneType);//预期:返回true,实际为false

        System.out.println("原型对象的爱好属性:"+prototype.getHobbies());
        System.out.println("克隆对象的爱好属性:"+cloneType.getHobbies());
        System.out.println(prototype.getHobbies() == cloneType.getHobbies());
    }
}

测试结果如下:

 

说明:

  上面的示例代码,虽然可以实现深克隆,但是一种硬编码方式。是基于ArrayList的特性而设计。

  在实际编码中使用并不常见,这里只是对原型模式源码应用的加深理解

 

1.5.原型模式的总结

1.5.1.实现深克隆的方案

1.序列化方案

2.转Json

 

原型模式,在实际编码中,很少使用到。原因如下:

1.复制对象

  apache提供了工具类,不需要自己去实现

  spring中也有实现

  jdk实现的浅克隆。

 

学习原型模式,是为了理解其中的底层原理,当代码出现问题时,可以分析其中的问题。

同样,学习设计模式,并不是一定要套用,也可以作为加深理解,提供更多选择。

 

1.5.2.原型模式的优点

  性能优良,jdk底层有实现原型模式,是基于内存二进制流的拷贝,比直接new一个对象性能提升更多。

  可以使用深克隆方式,保存对象的状态。得到新的实例,并且成员属性值省去大量设值操作。代码更优雅。

1.5.3.原型模式的缺点

  必须要配备克隆方法

  当对原有对象进行改造时,需要修改代码,违反开闭原则。

  深克隆,浅克隆要区分使用场景。

 

 

  

 

 

  

 

 

 

 

 

 

 

 

  

  

原型模式的应用场景

  》原型模式之深克隆与浅克隆

  》了解克隆是如何破坏单例的

  》原型模式的优缺点

  》掌握建造者模式和工厂模式的区别

posted @ 2020-05-07 13:14  我爱钻研  阅读(235)  评论(0)    收藏  举报