设计模式 - 原型模式

概念

  • 原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过克隆这些原型创建新的对象
  • 原型模式是一种对象创建型模式
  • 原型模式最典型的例子就是孙悟空用猴毛复制出和自己一模一样的分身来,通过原型对象克隆出多个一模一样的对象就是原型模式

编码

实例

  • 通过原型模式实现销售管理系统中的客户类(Customer
  • Customer.java
/**
 * @Description 客户
 */
public class Customer implements Cloneable {

    private String name;

    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

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

    /**
     * 重写克隆方法
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
  • Java中通过实现Cloneable接口重写clone()方法来实现原型模式
  • Test.java
/**
 * @Description 原型模式测试类
 */
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Customer customer = new Customer();
        customer.setName("张三");
        customer.setAddress("四川省成都市");
        System.out.println(customer);

        Customer customerClone = (Customer) customer.clone();
        customerClone.setAddress("浙江省杭州市");
        System.out.println(customerClone);
    }
}
  • 输出如下:
Customer{name='张三', address='四川省成都市'}
Customer{name='张三', address='浙江省杭州市'}
  • 通过克隆方法创建的对象是全新的对象,它们在内存中拥有新的地址

浅克隆

  • 如上例子,客户地址修改为Address
  • Customer.java
/**
 * @Description 客户
 */
public class Customer implements Cloneable {

    private String name;

    private Address address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

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

    /**
     * 重写克隆方法
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
  • Address.java
/**
 * @Description 地址
 */
public class Address {

    private String province;

    private String city;

    public Address(String province, String city) {
        this.province = province;
        this.city = city;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "Address{" +
                "province='" + province + '\'' +
                ", city='" + city + '\'' +
                '}';
    }
}
  • Test.java
/**
 * @Description 浅克隆测试类
 */
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Customer customer = new Customer();
        customer.setName("张三");
        customer.setAddress(new Address("四川省", "成都市"));
        System.out.println(customer);

        Customer customerClone = (Customer) customer.clone();
        System.out.println(customerClone);

        System.out.println(customer == customerClone);

        System.out.println("--------------");

        customer.getAddress().setProvince("浙江省");
        customer.getAddress().setCity("杭州市");

        System.out.println(customer);
        System.out.println(customerClone);
        System.out.println(customer == customerClone);

    }
}
  • 输出如下:
Customer{name='张三', address=Address{province='四川省', city='成都市'}}
Customer{name='张三', address=Address{province='四川省', city='成都市'}}
false
--------------
Customer{name='张三', address=Address{province='浙江省', city='杭州市'}}
Customer{name='张三', address=Address{province='浙江省', city='杭州市'}}
false
  • 可以发现,修改customercustomerClone的值也跟着修改了
  • Java语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括intdoublebyte等简单数据类型,引用类型包括类、接口、数组等复杂类型,浅克隆与深克隆的区别在于是否支持引用类型的成员变量的复制
  • 在浅克隆中,对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有被复制

深克隆

对于引用类型单独克隆

  • Customer.java
/**
 * @Description 客户
 */
public class Customer implements Cloneable {

    // ... 省略,同上

    /**
     * 深克隆方式一  对于引用类型单独克隆
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Customer customer = (Customer) super.clone();
        customer.address = (Address) customer.address.clone();
        return customer;
    }
}
  • Address.java
/**
 * @Description 地址
 */
public class Address implements Cloneable {

    // ... 省略,同上

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
  • 输出如下:
Customer{name='张三', address=Address{province='四川省', city='成都市'}}
Customer{name='张三', address=Address{province='四川省', city='成都市'}}
false
--------------
Customer{name='张三', address=Address{province='浙江省', city='杭州市'}}
Customer{name='张三', address=Address{province='四川省', city='成都市'}}
false

序列化实现深克隆

  • Customer.java
import java.io.Serializable;

/**
 * @Description 客户
 */
public class Customer implements Serializable {

    // ... 省略,同上

    /**
     * 深克隆方式二 序列化
     * @return
     */
    public Customer deepClone() throws Exception {
        // TODO 将对象写入流中
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        ObjectOutputStream oss = new ObjectOutputStream(bao);
        oss.writeObject(this);

        // TODO 将对象从流中取出
        ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (Customer) ois.readObject();
    }
}
  • Address.java
import java.io.Serializable;

/**
 * @Description 地址
 */
public class Address implements Serializable {

	// 需要实现序列化的对象必须实现```Serializable```接口
    // ... 省略,同上

}
  • 输出如下:
Customer{name='张三', address=Address{province='四川省', city='成都市'}}
Customer{name='张三', address=Address{province='四川省', city='成都市'}}
false
--------------
Customer{name='张三', address=Address{province='浙江省', city='杭州市'}}
Customer{name='张三', address=Address{province='四川省', city='成都市'}}
false
  • 在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将被复制

总结

  • 优点
1.原型模式是在内存中进行二进制流的拷贝,比直接new一个对象性能好
  • 缺点
1.必须配备克隆方法
2.对克隆复杂对象或对克隆出的对象进行复杂改造时容易引入风险
3.深拷贝、浅拷贝要运用得当
  • 适用场景
1.类初始化消耗较多资源
2.new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
3.构造函数比较复杂
4.循环体中生产大量对象时
  • 原型模式源代码
Object、ArrayList、HashMap、CacheKey

源码

- End -
- 个人学习笔记 -
- 仅供参考 -

posted @ 2022-03-28 18:24  Maggieq8324  阅读(39)  评论(0编辑  收藏  举报