17 原型模式
- 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
- 本质:克隆生成对象
- 优点
- 对客户端隐藏具体的实现类型
- 在运行时动态改变具体的实现类型
- 缺点
- 深度克隆方法实现会比较困难,必须要能够递归的让所有的相关对象都要正确的实现克隆
- 使用场景
- 如果一个系统想要独立于它想要使用的对象时,可以使用原型模式,让系统只面向接口编程,在系统需要新的对象的时候,可以通过克隆原型来得到
- 如果需要实例化的类是在运行时刻动态指定时,可以使用原型模式,通过克隆原型来得到需要的实例
- 类图
17.1 代码
-
OrderApi
/** * 订单的接口,声明了可以克隆自身的方法 */ public interface OrderApi { public int getOrderProductNum(); public void setOrderProductNum(int num); /** * 克隆方法 * @return 订单原型的实例 */ public OrderApi cloneOrder(); }
-
PersonalOrder
/** * 个人订单对象 */ public class PersonalOrder implements OrderApi{ private String customerName; private String productId; private int orderProductNum = 0; public int getOrderProductNum() { return this.orderProductNum; } public void setOrderProductNum(int num) { this.orderProductNum = num; } public String getCustomerName() { return customerName; } public void setCustomerName(String customerName) { this.customerName = customerName; } public String getProductId() { return productId; } public void setProductId(String productId) { this.productId = productId; } public String toString(){ return "本个人订单的订购人是="+this.customerName+",订购产品是="+this.productId+",订购数量为="+this.orderProductNum; } public OrderApi cloneOrder() { //创建一个新的订单,然后把本实例的数据复制过去 PersonalOrder order = new PersonalOrder(); order.setCustomerName(this.customerName); order.setProductId(this.productId); order.setOrderProductNum(this.orderProductNum); return order; } }
-
EnterpriseOrder
/** * 企业订单对象 */ public class EnterpriseOrder implements OrderApi{ private String enterpriseName; private String productId; private int orderProductNum = 0; public int getOrderProductNum() { return this.orderProductNum; } public void setOrderProductNum(int num) { this.orderProductNum = num; } public String getEnterpriseName() { return enterpriseName; } public void setEnterpriseName(String enterpriseName) { this.enterpriseName = enterpriseName; } public String getProductId() { return productId; } public void setProductId(String productId) { this.productId = productId; } public String toString(){ return "本企业订单的订购企业是="+this.enterpriseName+",订购产品是="+this.productId+",订购数量为="+this.orderProductNum; } public OrderApi cloneOrder() { //创建一个新的订单,然后把本实例的数据复制过去 EnterpriseOrder order = new EnterpriseOrder(); order.setEnterpriseName(this.enterpriseName); order.setProductId(this.productId); order.setOrderProductNum(this.orderProductNum); return order; } }
-
OrderBusiness
/** * 处理订单的业务对象 */ public class OrderBusiness { /** * 创建订单的方法 * @param order 订单的接口对象 */ public void saveOrder(OrderApi order){ //1:判断当前的预定产品数量是否大于1000 while(order.getOrderProductNum() > 1000){ //如果OrderApi接口中没有cloneOrder方法, 那么你在这个位置由于并不知道cloneOrder的真实类型,所以无法创建一个cloneOrder对象出来 //2:如果大于,还需要继续拆分 //2.1再新建一份订单,跟传入的订单除了数量不一样外,其它都相同 OrderApi newOrder = order.cloneOrder(); //然后进行赋值,产品数量为1000 newOrder.setOrderProductNum(1000); //2.2原来的订单保留,把数量设置成减少1000 order.setOrderProductNum(order.getOrderProductNum()-1000); //然后是业务功能处理,省略了,打印输出,看一下 System.out.println("拆分生成订单=="+newOrder); } //3:不超过,那就直接业务功能处理,省略了,打印输出,看一下 System.out.println("订单=="+order); } }
-
OrderClient
public class OrderClient { public static void main(String[] args) { //创建订单对象,这里为了演示简单,直接new了 PersonalOrder op = new PersonalOrder(); //设置订单数据 op.setOrderProductNum(2925); op.setCustomerName("张三"); op.setProductId("P0001"); //这里获取业务处理的类,也直接new了,为了简单,连业务接口都没有做 OrderBusiness ob = new OrderBusiness(); //调用业务来保存订单对象 ob.saveOrder(op); } }
17.2 java中的原型模式
-
java语言天生就将原型模式设计到了语言模型中
-
Object类本身就带有clone方法,但该方法的权限为protected,所以一个类想在该类外部使用clone方法,必须重写该方法,从而扩大clone方法权限
-
同时类如果想使用Object的clone方法,还需要实现Cloneable接口
-
j调用Object的clone方法,构造器不会被执行
-
Object的clone方法只拷贝对象本身,不对其成员变量进行拷贝,只拷贝成员变量为基本类型以及String的那些
-
想被克隆的对象的成员变量,不能用final修饰
-
通常可以利用序列化与反序列化进行深复制
-
PersonalOrder
//1. 必须实现Cloneable接口
public class PersonalOrder implements Cloneable , OrderApi{
//2. 重写Object的clone方法,以扩大使用范围
public Object clone(){
PersonalOrder obj=null;
try {
//调用Object的clone方法
obj =(PersonalOrder)super.clone();
//Object的clone方法只拷贝对象本身,不对其成员变量进行拷贝,所以如果想进行深克隆,需要对引用类型成员变量进行特殊处理
obj.setProduct((Product)this.product.clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}