原型模式

发布时间 2024-01-13 10:04:28作者: wangzhilei

原型模式

是利用克隆方法克隆出新的对象.

  • 定义:原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
  • 特点:不需要知道任何创建的细节,不调用构造函数
  • 适用场景:
    •   类初始化消耗较多资源
    •   new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
    •   构造函数比较复杂
    •   循环体中产生大量对象时
  • 优点:
    •   原型模式性能比直接new一个对象性能高
    •   简化创建过程
  • 缺点:
    •   必须配备克隆方法,这个模式的核心就是克隆方法,通过这个方法进行对象的拷贝
    •   对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险
    •   深拷贝和浅拷贝要运用得当,否则很容易引入风险

浅拷贝

拷贝对象中嵌套的对象是引用,指向同一个内存地址,而非独立内存空间。

浅拷贝得到新的对象A+,A+的内存地址是独立的,但其内部嵌套对象B却不是独立的内存,其仍然指向先前A对象中的B对象内存地址。改变A+的成员变量不会影响原有的A对象,但改变A+中的B对象成员变量却会影响原有A对象的B对象成员变量。

IMG_256

实现方法

实现Cloneable接口,重写基类Object的clone方法。

注意:

clone是一个native方法,其要比对象之间的直接引用性能要高。

native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(C/C++)实现的文件中。Java本身不能对操作系统底层进行访问和操作,但可以通过JNI接口调用其他语言来实现对底层的访问。

深拷贝

即对象完全克隆;不管A对象中包含了多少个对象,包含的对象又嵌套多少个对象,都完全将其进行复制。复制后新对象A+,不管对A+及其内部对象做了什么操作,都不会改变原有对象A。

IMG_256

实现方法

实现Serializable接口(包括嵌套对象),自定义克隆方法,利用对象的字节流进行序列化和反序列化得到新的对象。

代码

ESexType

/**
 * <p>性别枚举类</p>
 */
public enum ESexType {

    /**
     * 枚举对象
     */
    MALE("男",0),
    FEMALE("女",1);


    private final  String name ;
    private final  Integer value;

    /**
     * 枚举构造器
     * @param name 性别名称
     * @param value 性别值
     */
    ESexType(String name, Integer value){
        this.name = name;
        this.value = value;
    }

    /**
     * 通过name获取对应的枚举对象
     * @param name 类型名称
     */
    public static ESexType getEnum(String name){
        for(ESexType sexEnum : ESexType.values()){
            if(name.equals(sexEnum.getName())){
                return  sexEnum;
            }
        }
        return  null;
    }

    /**
     * 通过value获取对应的枚举对象
     * @param value 类型值
     */
    @JsonCreator
    public static ESexType getEnum(Integer value){
        for(ESexType sexEnum : ESexType.values()){
            if(value.equals(sexEnum.getValue())){
                return  sexEnum;
            }
        }
        return  null;
    }


    public String getName() {
        return name;
    }

    @JsonValue
    public Integer getValue() {
        return value;
    }
}

EWorkType

/**
 * <p>作业枚举类</p>
 */
public enum EWorkType {

    YU_WEN("语文",0),
    SHU_XUE("数学",1),
    YING_YU("英语",2),
    WU_LI("物理",3),
    HUA_XUE("化学",4),
    SHENG_WU("生物",5);

    private final  String name ;
    private final  Integer value;

    /**
     * 枚举构造器
     * @param name 作业类型名称
     * @param value 作业类型值
     */
    EWorkType(String name, Integer value){
        this.name = name;
        this.value = value;
    }


    /**
     * 通过name获取对应的枚举对象
     * @param name 类型名称
     */
    public static EWorkType getEnum(String name){
        for(EWorkType typeEnum : EWorkType.values()){
            if(name.equals(typeEnum.getName())){
                return  typeEnum;
            }
        }
        return  null;
    }

    /**
     * 通过value获取对应的枚举对象
     * @param value 类型值
     */
    @JsonCreator
    public static EWorkType getEnum(Integer value){
        for(EWorkType typeEnum : EWorkType.values()){
            if(value.equals(typeEnum.getValue())){
                return  typeEnum;
            }
        }
        return  null;
    }


    public String getName() {
        return name;
    }

    @JsonValue
    public Integer getValue() {
        return value;
    }
}

PupilStudent

/**
 * <p>小学生 == 如果要实现对象的深度拷贝,嵌套引用类型的对象类必须也实现Serializable接口</p>
 */
@Data
public class PupilStudent implements Serializable {
    
   private static final long serialVersionUID = 1L;

   /**学号*/
    private Long sNo;

    /**年级*/
    private Integer sClass;

    /**姓名*/
    private String name;

    /**年龄*/
    private Integer age;

    /**性别*/
    private ESexType sex = ESexType.MALE;
}

HomeWork

/**
 * <p>小学生的家庭作业</p>
 */
@Data
public class HomeWork implements Cloneable, Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 作业类型
     */
    private EWorkType type = EWorkType.YU_WEN;

    /**
     * 完成时间
     */
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private Date finishTime;

    /**
     * 作业页码数【作业量】
     */
    private Integer pages = 0;

    /**
     * 完成者
     */
    private PupilStudent pupilStudent;

    /**
     * 对象浅拷贝  == 对象中按值类型传递部分均能完美拷贝走,但是按引用类型传递部分则拷贝不走
     *
     * @return HomeWork
     * @throws CloneNotSupportedException
     */
    @Override
    public HomeWork clone() throws CloneNotSupportedException {
        return (HomeWork) super.clone();
    }

    /**
     * 深度拷贝 == 不管你对象中是值类型部分,还是引用类型部分,我全部拿走
     * 对象字节流的序列与反序列化 ==> 对象完全、深度、彻彻底底的Copy!!!
     *
     * @return HomeWork
     */
    public HomeWork deepClone() {

        // Anything 都是可以用字节流进行表示!
        HomeWork homeWork = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            // 将当前的对象写入baos【输出流 -- 字节数组】里
            oos.writeObject(this);

            // 从输出字节数组缓存区中拿到字节流
            byte[] bytes = baos.toByteArray();
            // 创建一个输入字节数组缓冲区
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            // 创建一个对象输入流
            ObjectInputStream ois = new ObjectInputStream(bais);
            // 下面将反序列化字节流 == 重新开辟一块空间存放反序列化后的对象
            homeWork = (HomeWork) ois.readObject();

        } catch (Exception e) {
            System.out.println(e.getClass() + ":" + e.getMessage());
        }
        return homeWork;
    }

    @Override
    public String toString() {
        return String.format("类型:%s,页数:%s,完成时间:%s," +
                        "完成者:%s,学号:%d,年级:%d,年龄:%d,性别:%s", this.type.getName()
                , this.pages, DateUtils.date2Str(this.finishTime), this.pupilStudent.getName(),
                this.pupilStudent.getsNo(), this.pupilStudent.getsClass(),
                this.pupilStudent.getAge(), this.pupilStudent.getSex().getName());
    }
}

测试

/**
 * <p>原型模式测试 == 两种方式?</p>
 */
public class PrototypeTest {
    public static void main(String[] args) throws CloneNotSupportedException{

        // 原型 == 我们创建一个已经由小学生【刘晓然】完成的作业对象
        HomeWork homeWork = new HomeWork();
        // 设置作业信息
        homeWork.setType(EWorkType.WU_LI);
        homeWork.setPages(12);
        homeWork.setFinishTime(new Date());
        // 设置小学生信息 == 刘晓然
        PupilStudent pupilStudent  = new PupilStudent();
        pupilStudent.setsNo(1001L);
        pupilStudent.setName("刘晓然");
        pupilStudent.setAge(10);
        pupilStudent.setSex(ESexType.FEMALE);
        pupilStudent.setsClass(4);
        homeWork.setPupilStudent(pupilStudent);

        // 1、原型模式第一种 == 作业对象浅拷贝测试
        HomeWork ykHomeWork = shallowCopy(homeWork);
        System.out.println("刘晓然的作业:\n"+homeWork);
        System.out.println("我的作业:\n"+ykHomeWork);

        /**
         * 以上输出结果为:
         * 刘晓然的作业:
         * 类型:物理,页数:12,完成时间:2018年11月08日 14时03分45秒,完成者:Appleyk,学号:1002,年级:4,年龄:10,性别:男
         * 我的作业:
         * 类型:物理,页数:12,完成时间:2018年11月09日 14时03分45秒,完成者:Appleyk,学号:1002,年级:4,年龄:10,性别:男
         */


        // 2、原型模式第二种 == 作业对象深拷贝测试
        System.out.println("=========================================分割线");

        /**
         * 假设有第三个同学,要抄作业,这个同学假设叫"张聪明"
         */

        HomeWork zhangHomeWork = deepCopy(ykHomeWork);
        System.out.println("Appleyk的作业:\n"+ykHomeWork);
        System.out.println("张聪明的作业:\n"+zhangHomeWork);

        /**
         * 以上输出结果为:
         * Appleyk的作业:
         * 类型:物理,页数:12,完成时间:2018年11月09日 14时16分11秒,完成者:Appleyk,学号:1002,年级:4,年龄:10,性别:男
         * 张聪明的作业:
         * 类型:物理,页数:12,完成时间:2018年11月09日 14时16分11秒,完成者:张聪明,学号:1003,年级:4,年龄:10,性别:男
         */
    }

    /**
     * 对象浅拷贝
     * @param homeWork
     * @return
     * @throws CloneNotSupportedException
     */
    public static  HomeWork shallowCopy(HomeWork homeWork) throws  CloneNotSupportedException{

        HomeWork myHomeWork = homeWork.clone();

        myHomeWork.setFinishTime(DateUtils.addDays(1));
        PupilStudent mySelf = myHomeWork.getPupilStudent();
        mySelf.setsNo(1002L);
        mySelf.setName("Appleyk");
        mySelf.setSex(ESexType.MALE);
       
        return  myHomeWork;
    }

    /**
     * 对象深度拷贝
     * @param homeWork
     * @return
     * @throws CloneNotSupportedException
     */
    public static  HomeWork deepCopy(HomeWork homeWork) throws  CloneNotSupportedException{

        HomeWork myHomeWork = homeWork.deepClone();
        myHomeWork.setFinishTime(DateUtils.addDays(1));
        PupilStudent mySelf = myHomeWork.getPupilStudent();
        mySelf.setsNo(1003L);
        mySelf.setName("张聪明");
        mySelf.setSex(ESexType.MALE);
        return  myHomeWork;
    }
}