版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012099869/article/details/78765204
主要内容来自《研磨设计模式》
一、模式定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
原型的本质:克隆生成对象。创建型模式。
二、模式结构
三、代码示例
功能描述:当订单数量大于1000时,拆分订单,直至订单数量小于1000。
对外统一的订单接口:
package com.kascend.test.prototype;
/**
* 订单接口
*
* @author wengliemiao
*/
public interface OrderApi {
/**
* 获取订单中产品数量
*
* @return 订单中产品数量
*/
int getOrderProNum();
/**
* 设置订单中产品数量
*
* @param num 订单中产品数量
*/
void setOrderProNum(int num);
/**
* 克隆订单
*
* @return 订单原型的实例
*/
OrderApi cloneOrder();
}
具体的个人订单实现类:
package com.kascend.test.prototype;
/**
* 个人订单对象
*
* @author wengliemiao
*/
public class PersonalOrder implements OrderApi {
private String customerName;
private int orderProductNum = 0;
private Product product = null;
@Override
public int getOrderProNum() {
return this.orderProductNum;
}
@Override
public void setOrderProNum(int num) {
this.orderProductNum = num;
}
/**
* 获取 customerName
*
* @return customerName
*/
public String getCustomerName() {
return customerName;
}
/**
* 设置 customerName
*
* @param customerName
*/
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
/**
* 获取 product
*
* @return product
*/
public Product getProduct() {
return product;
}
/**
* 设置 product
*
* @param product
*/
public void setProduct(Product product) {
this.product = product;
}
@Override
public String toString() {
return "个人订单信息{" +
"customerName='" + customerName + '\'' +
", orderProductNum=" + orderProductNum +
", product=" + product +
'}';
}
@Override
public OrderApi cloneOrder() {
PersonalOrder personalOrder = new PersonalOrder();
personalOrder.setCustomerName(this.customerName);
personalOrder.setOrderProNum(this.orderProductNum);
personalOrder.setProduct((Product) this.product.cloneProduct()); // 深克隆
return personalOrder;
}
}
其中产品信息对象如下(为演示深克隆):
package com.kascend.test.prototype;
/**
* 克隆产品自身接口
*
* @author wengliemiao
*/
public interface ProductPrototype {
/**
* 克隆自身产品
*
* @return 从自身产品克隆出来的对象
*/
ProductPrototype cloneProduct();
}
package com.kascend.test.prototype;
/**
* 产品信息对象
*
* @author wengliemiao
*/
public class Product implements ProductPrototype {
private String productId;
private String name;
public Product() {
}
public Product(String productId, String name) {
this.productId = productId;
this.name = name;
}
/**
* 获取 productId
*
* @return productId
*/
public String getProductId() {
return productId;
}
/**
* 设置 productId
*
* @param productId
*/
public void setProductId(String productId) {
this.productId = productId;
}
/**
* 获取 name
*
* @return name
*/
public String getName() {
return name;
}
/**
* 设置 name
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
@Override
public ProductPrototype cloneProduct() {
Product product = new Product();
product.setProductId(this.productId);
product.setName(this.name);
return product;
}
@Override
public String toString() {
return "Product{" +
"productId='" + productId + '\'' +
", name='" + name + '\'' +
'}';
}
}
创建订单类:
package com.kascend.test.prototype;
/**
* 订单业务处理类
*
* @author wengliemiao
*/
public class OrderBusiness {
/**
* 创建订单方法
*
* @param order 订单信息
*/
public void saveOrder(OrderApi order) {
while (order.getOrderProNum() > 1000) {
// 拆分订单(克隆)
OrderApi newOrder = order.cloneOrder();
// 赋值
newOrder.setOrderProNum(1000);
// 原来订单数量减去 1000
order.setOrderProNum(order.getOrderProNum() - 1000);
System.out.println("拆分订单: " + newOrder);
}
System.out.println("订单: " + order);
}
public static void main(String[] args) {
// 个人订单
PersonalOrder personalOrder = new PersonalOrder();
personalOrder.setCustomerName("wlmmm");
personalOrder.setProduct(new Product("1", "产品1"));
personalOrder.setOrderProNum(6666);
// 创建订单
OrderBusiness ob = new OrderBusiness();
ob.saveOrder(personalOrder);
}
}
输出为:
四、Java 克隆
Java 中提供了 clone 方法,需要克隆功能的类,只需实现 java.lang.Cloneable 接口,这个接口没有需要实现的方法,是一个标识接口。
因此可使用 Java 克隆实现原型模式。
修改前面的实现如下:
package com.kascend.test.prototype;
/**
* 订单接口
*
* @author wengliemiao
*/
public interface OrderApiCloneable {
/**
* 获取订单中产品数量
*
* @return 订单中产品数量
*/
int getOrderProNum();
/**
* 设置订单中产品数量
*
* @param num 订单中产品数量
*/
void setOrderProNum(int num);
}
package com.kascend.test.prototype;
/**
* 个人订单对象
*
* @author wengliemiao
*/
public class PersonalOrderCloneable implements Cloneable, OrderApiCloneable {
private String customerName = "";
private String productId = "";
private int orderProNum = 0;
@Override
public int getOrderProNum() {
return this.orderProNum;
}
@Override
public void setOrderProNum(int num) {
this.orderProNum = num;
}
/**
* 获取 customerName
*
* @return customerName
*/
public String getCustomerName() {
return customerName;
}
/**
* 设置 customerName
*
* @param customerName
*/
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
/**
* 获取 productId
*
* @return productId
*/
public String getProductId() {
return productId;
}
/**
* 设置 productId
*
* @param productId
*/
public void setProductId(String productId) {
this.productId = productId;
}
@Override
protected Object clone() {
Object obj = null;
try {
obj = super.clone();
} catch (Exception e) {
}
return obj;
}
@Override
public String toString() {
return "个人订单信息{" +
"customerName='" + customerName + '\'' +
", productId='" + productId + '\'' +
", orderProNum=" + orderProNum +
'}';
}
}
package com.kascend.test.prototype;
/**
* Java 克隆实现原型模式测试类
*
* @author wengliemiao
*/
public class OrderCloneTest {
public static void main(String[] args) {
PersonalOrderCloneable order = new PersonalOrderCloneable();
order.setCustomerName("wlmmm");
order.setProductId("1");
order.setOrderProNum(100);
System.out.println("克隆前的实例: " + order);
PersonalOrderCloneable cloneOrder = (PersonalOrderCloneable) order.clone();
cloneOrder.setOrderProNum(200);
System.out.println("克隆的实例: " + cloneOrder);
System.out.println("克隆后的实例: " + order);
}
}
输出为:
如何实现 Java 深拷贝参看文章: Java 浅拷贝与深拷贝
五、模式优缺点和使用时机
优点:
- 对客户端隐藏具体的实现类型。可减少客户端对具体实现类型的依赖
- 在运行时动态改变具体的实现类型。由客户来注册符合原型接口的实现类型,也可动态地改变具体的实现类型,但接口不变。
缺点:
每个原型的子类都必须实现 clone
的操作,尤其在包含引用类型的对象时,clone
方法会比较麻烦,必须能够递归地让所有的相关对象正确地实现克隆。
使用时机:
- 如果一个系统想要独立于它想要使用的对象时,可以使用原型模式,让系统只面向接口编程。
- 如果需要实例化的类时在运行时刻动态指定时,可以使用原型模式,通过克隆原型来得到需要的实例。