场景问题
考虑实际应用:订单处理系统
现在有着这样一种系统,里面有一个保存订单的业务功能,在这个功能中,客户有这样一个需求当订单产品超过1000时候,就需要把订单拆分为两分订单,如果拆分之后还是超过1000,那么继续拆分,直到每笔订单数量不超过1000
解决方案
使用原型模式来解决问题
定义 用原型实例类指定创建对象的种类,并通过拷贝这些原型创建新的对象
思路
原型模式要求对象实现一个可以克隆自己的接口,这样就可以通过拷贝或者式克隆一个实例对象本身来创建一个新的实例,如果把这个接口定义为一个接口,看起来就好像是通过接口来创建了新的接口对象,这样,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,也不关心他的具体实现,只要他实现了克隆自身的方法,就可以通过这个方法来获取新对象,不需要通过new创建
结构说明
实例代码
package com.company;
// 订单接口
public interface OrderApi {
public int getOrderProductNum();
public void setOrderProductNum(int num);
public OrderApi cloneOrder();
}
package com.company;
/**
* @Author tan 个人订单类
**/
public class PersonalOrder implements OrderApi{
private int orderProductNum = 0;
@Override
public int getOrderProductNum() {
return orderProductNum;
}
@Override
public void setOrderProductNum(int orderProductNum) {
this.orderProductNum = orderProductNum;
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
@Override
public String toString() {
return "PersonalOrder{" +
"orderProductNum=" + orderProductNum +
", productId='" + productId + '\'' +
", customerName='" + customerName + '\'' +
'}';
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
private String productId;
private String customerName;
@Override
public OrderApi cloneOrder() {
PersonalOrder order = new PersonalOrder();
order.setCustomerName(this.customerName);
order.setOrderProductNum(this.orderProductNum);
order.setProductId(this.productId);
return null;
}
}
package com.company;
public class Client {
public void saveOrder(OrderApi order){
while (order.getOrderProductNum()>1000){
OrderApi newOrder = order.cloneOrder();
newOrder.setOrderProductNum(1000);
order.setOrderProductNum(order.getOrderProductNum()-1000);
System.out.println("拆分订单");
}
}
}
模式详解
原型模式的功能
原型模式的功能其实包括两个方面,一个是通过克隆来创建新的对象实例,另外一个是为克隆出来的新的对象实例复制到原型实例属性的值,他实现的主要功能是:通过克隆来创建新的对象实例
原型与new
克隆与new的不同之处在于,new 一个对象,一般属性是没有值的,或者只是默认值,但是如果是克隆一个对象,通常属性是有值的,属性的值就是原型对象实例克隆的时候,原型对象实例属性的值
原型实例和克隆的实例,本质上是一个不同的实例,克隆完成之后他们是互不关连的,就是克隆完成之后我们对于克隆对象的属性修改是不会影响到原型对象的
调用顺序
java中提供的克隆方法
java语言中以提供了clone方法,定义在Object中
浅克隆和深克隆
浅克隆:只负责克隆按值传递的数据(比如基本数据类型)
深克隆:除了浅克隆以外的,还复制引用类型的克隆,基本上就是被克隆实例所有的属性数据都会被克隆出来
深度克隆还有一个特点,如果被克隆对象里面的属性数据是引用类型,也就是属性的类型也是对象,则需要一直递归的克隆下去,这也意味着要想深度克隆成功,必须要整个克隆所涉及的方法实现正确的克隆方式,如果其中有个克隆失败,那么就会导致全局的克隆失败
原型模式优缺点
优
对客户端隐藏具体的实现类型
原型模式的客户端只知道原型接口的类型,并不知道具体的实现类型,少了客户端对这些具体实现类型的依赖
在运行时可以动态的改变具体的实现类型
原型模式可以在运行时期,由客户来注册符合原型接口的实现类型,也可以动态的改变具体的实现类型,看起来接口没有任何变化,但是其实运行的以及是另一个类实例
缺
每一个原型的子类都必须实现clone的操作,尤其在包含引用类型的对象时,clone会比较麻烦
本质
克隆生成对象,克隆是手段,目的是生成新的对象实例,正是因为原型的目的是为了生成新的对象实例,原型模式通常是被归于类的创建型模式