1. 定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
2. 类图
Prototype:声明一个克隆自身的接口,用来约束想要克隆自己的类,要求它们都要实现这里定义的克隆方法。
ConcretePrototype:实现Prototype接口的类,这些类真正实现了克隆本身的功能。
Client:使用原型的客户端,首先要获取到原型实例对象,然后通过原型实例对象克隆本身来创建新的实例对象。
3. 实例
package com.jerry.designpattern; /** * * @author Jerry * @date 2015年1月20日 上午9:41:10 */ public interface OrderApi { /** * 获得产品数量 * @return */ int getProductNumber(); /** * 设置产品数量 * @param num */ void setProductNumber(int num); /** * 克隆订单 * @return */ OrderApi cloneOrder(); } package com.jerry.designpattern; /** * * @author Jerry * @date 2015年1月20日 上午10:01:09 */ public class OrderBusiness { /** * 保存订单 * 如果存在订单的产品数量大于1000,则生成新的订单,直至小于数量1000 * @param order */ public void saveOrder(OrderApi order) { while (order.getProductNumber() > 1000) { System.out.println("订单数量为" + order.getProductNumber() + ",超过1000,需要拆分订单。"); OrderApi newOrder = order.cloneOrder(); newOrder.setProductNumber(1000); order.setProductNumber(order.getProductNumber() - 1000); System.out.println("拆分出新的订单:" + newOrder); } System.out.println("订单:" + order); } } package com.jerry.designpattern; /** * * @author Jerry * @date 2015年1月20日 上午9:53:50 */ public class PersonalOrder implements OrderApi{ private String customerName; private int productId; private int productNumber; public String getCustomerName() { return customerName; } public void setCustomerName(String customerName) { this.customerName = customerName; } public int getProductId() { return productId; } public void setProductId(int productId) { this.productId = productId; } @Override public int getProductNumber() { // TODO Auto-generated method stub return this.productNumber; } @Override public void setProductNumber(int productNumber) { // TODO Auto-generated method stub this.productNumber = productNumber; } @Override public OrderApi cloneOrder() { // TODO Auto-generated method stub PersonalOrder order = new PersonalOrder(); order.setCustomerName(this.customerName); order.setProductId(this.productId); order.setProductNumber(this.productNumber); return order; } @Override public String toString() { // TODO Auto-generated method stub return "个人订单人为:" + this.customerName + ",订购产品为:" + this.productId + ",订购产品数量为:" + this.productNumber; } } package com.jerry.designpattern; /** * * @author Jerry * @date 2015年1月20日 上午9:57:16 */ public class EnterpriseOrder implements OrderApi{ private String enterpriseName; private int productId; private int productNumber; public String getEnterpriseName() { return enterpriseName; } public void setEnterpriseName(String enterpriseName) { this.enterpriseName = enterpriseName; } public int getProductId() { return productId; } public void setProductId(int productId) { this.productId = productId; } @Override public int getProductNumber() { // TODO Auto-generated method stub return this.productNumber; } @Override public void setProductNumber(int productNumber) { // TODO Auto-generated method stub this.productNumber = productNumber; } @Override public OrderApi cloneOrder() { // TODO Auto-generated method stub EnterpriseOrder order = new EnterpriseOrder(); order.setEnterpriseName(this.enterpriseName); order.setProductId(this.productId); order.setProductNumber(this.productNumber); return order; } } package com.jerry.designpattern; /** * * @author Jerry * @date 2015年1月20日 上午10:07:24 */ public class Client { public static void main(String[] args) { PersonalOrder order = new PersonalOrder(); order.setCustomerName("Jerry"); order.setProductId(123456); order.setProductNumber(42223); OrderBusiness orderBusiness = new OrderBusiness(); orderBusiness.saveOrder(order); } }
4. 原型模式的功能
原型模式的功能实际上包含两个方面,第一个是通过克隆来创建新的对象实例,第二个是为克隆出来的对象实例复制原型对象实例属性值。
原型模式从某种意义上来说,就像是new操作,只是类似于new操作,new一个对象实例,一般属性是没有值或者是默认值,而克隆得到的对象实例,通常都是有值的,一般都是原型对象实例克隆时的值。
5. 深度克隆和浅度克隆
原型模式是通过克隆创建新的对象实例,因此涉及到深度克隆和浅度克隆的问题,那什么是深度克隆和浅度克隆呢?
浅度克隆:只负责克隆按值传递的数据,比如基本数据类型、String类型。
深度克隆:除了浅度克隆要克隆的值外,还负责克隆引用类型的数据,基本上就是被克隆实例所有的属性数据都会被克隆出来。
实现深度克隆也不是很复杂,只需要实现所有的引用类型的克隆功能,创建时调用它的克隆方法就可以了。
6. 原型的优缺点
原型的优点有:
a) 对客户端隐藏具体的实现类型
b) 在运行时动态改变具体的实现类型
原型最大的缺点是所有类都要实现克隆方法,特别存在引用类型,比较麻烦。