Java设计模式之原型模式

1. 原型模式概述

1.1 原型概念

原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。

1.2 原型优缺点

1.原型模式的优点:
Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。

2.原型模式的缺点:
需要为每一个类都配置一个 clone 方法 clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。

1.3 使用场景

定义已经说得很清楚,便于创建相同对象或者相似对象。

1.4 深浅克隆区别

原型模式的克隆分为浅克隆和深克隆。
1.浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。

2.深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

2. 原型模式实现

/**
 * @author zrj
 * @date 2020/12/9
 * @since V1.0
 **/
@Getter
@Setter
@ToString
public class Employee implements Cloneable {
    private String name;
    private int age;
    /**
     * 地址对象
     * 浅复制:Address未实现CloneAble接口,没有重写clone方法
     */
    private Address address;

    /**
     * 工作经历
     * 深复制:WorkStation实现CloneAble接口,重写clone方法
     */
    private WorkStation workStation;

    public Employee(String name, int age, Address address, WorkStation workStation) {
        this.name = name;
        this.age = age;
        this.address = address;
        this.workStation = workStation;
    }

    public void setAddressInfo(String code, String name) {
        address.setAddressCode( code );
        address.setAddressName( name );
    }

    public void setWorkStation(String code, String name) {
        workStation.setWorkCode( code );
        workStation.setWorkStation( name );
    }

    public void showAddress() {
        System.out.println( "------------------------------" );
        System.out.println( "姓名:" + name + ",年龄:" + age + ",地址:" + address.getAddressName() + ",工作:" + workStation.getWorkStation() );
    }

    @Override
    public Employee clone() throws CloneNotSupportedException {
        return (Employee) super.clone();
    }

    public Employee deepClone() throws CloneNotSupportedException {
        Employee deepClone = (Employee) super.clone();
        deepClone.setWorkStation( workStation.clone() );
        return deepClone;
    }
}


/**
 * @author zrj
 * @date 2020/12/9
 * @since V1.0
 **/
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Address {
    private String addressCode;
    private String addressName;
}


/**
 * @author zrj
 * @date 2020/12/9
 * @since V1.0
 **/
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class WorkStation implements Cloneable{
    private String workCode;
    private String workStation;

    @Override
    public WorkStation clone() throws CloneNotSupportedException {
        return (WorkStation) super.clone();
    }

}



/**
 * 浅复制
 * 只能实现基本数据类型拷贝,对象,集合,容器等均为对象地址的引用
 * 预期结果是,cloneA,cloneB都是克隆与Employee对象,设置三个不同地址,打印三个不同地址
 * 实际结果是,三个地址相同,浅拷贝拷贝的是对象的引用,所以浅复制三个地址的值是一样的。
 * <p>
 * 深复制
 * 两个条件,引用的对象也实现了Cloneable接口,并所复制的对象也重新set了引用对象的clone对象。
 *
 * @author zrj
 * @date 2020/12/5
 * @since V1.0
 **/
public class PrototypeTest {

    @Test
    public void prototypeTest() throws CloneNotSupportedException {
        Employee employee = new Employee( "艾伦", 18, new Address(), new WorkStation() );
        employee.setAddressInfo( "1001", "西安" );
        employee.setWorkStation( "1", "开发" );
        System.out.println( "原对象:" + employee.toString() );

        System.out.println( "-------------------浅复制---------------------" );
        Employee cloneA = employee.clone();
        cloneA.setAddressInfo( "1002", "上海" );
        employee.setWorkStation( "2", "前端" );
        System.out.println( "克隆对象A:" + cloneA.toString() );

        Employee cloneB = employee.clone();
        cloneA.setAddressInfo( "1003", "南京" );
        employee.setWorkStation( "3", "测试" );
        System.out.println( "克隆对象B:" + cloneB.toString() );

        employee.showAddress();
        cloneA.showAddress();
        cloneB.showAddress();

        System.out.println( "-------------------深复制---------------------" );

        Employee cloneC = employee.deepClone();
        cloneA.setAddressInfo( "1002", "北极" );
        employee.setWorkStation( "2", "自然学家" );
        System.out.println( "cloneC:" + cloneA.toString() );

        Employee cloneD = employee.deepClone();
        cloneA.setAddressInfo( "1003", "南极" );
        employee.setWorkStation( "3", "物理学家" );
        System.out.println( "cloneD:" + cloneB.toString() );

        employee.showAddress();
        cloneC.showAddress();
        cloneD.showAddress();
    }
}

输出结果:
原对象:Employee(name=艾伦, age=18, address=Address(addressCode=1001, addressName=西安), workStation=WorkStation(workCode=1, workStation=开发))
-------------------浅复制---------------------
克隆对象AEmployee(name=艾伦, age=18, address=Address(addressCode=1002, addressName=上海), workStation=WorkStation(workCode=2, workStation=前端))
克隆对象BEmployee(name=艾伦, age=18, address=Address(addressCode=1003, addressName=南京), workStation=WorkStation(workCode=3, workStation=测试))
------------------------------
姓名:艾伦,年龄:18,地址:南京,工作:测试
------------------------------
姓名:艾伦,年龄:18,地址:南京,工作:测试
------------------------------
姓名:艾伦,年龄:18,地址:南京,工作:测试
-------------------深复制---------------------
cloneC:Employee(name=艾伦, age=18, address=Address(addressCode=1002, addressName=北极), workStation=WorkStation(workCode=2, workStation=自然学家))
cloneD:Employee(name=艾伦, age=18, address=Address(addressCode=1003, addressName=南极), workStation=WorkStation(workCode=3, workStation=物理学家))
------------------------------
姓名:艾伦,年龄:18,地址:南极,工作:物理学家
------------------------------
姓名:艾伦,年龄:18,地址:南极,工作:测试
------------------------------
姓名:艾伦,年龄:18,地址:南极,工作:自然学家
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值