原型模式
定义
使用原型实例指定创建对象的种类,然后通过拷贝这些原型来对象。
原型模式的本质:克隆生成对象
克隆是手段,目的就是生成新的对象。正是因为原型的目的,所以原型模式被归类到创建型模式
示例代码
原型模式与工厂方法模式很像,于是通过工厂方法模式引导出原型模式。
步骤1. 工厂方法模式,我们新建两个接口 ISplitter接口和ISplitterFactory接口。代码如下:
public interface ISplitter {
void split(String filePath, int fileNumber);
}
public interface ISplitterFactory {
public ISplitter createSplitter();
}
在原型模式中,我们把这两个接口合并,去掉ISplitterFactory接口,并把ISplitterFactory接口中的createSplitter方法改为clone方法
如下代码:
public interface ISplitter {
void split(String filePath,int fileNumber);
// 原型模式
ISplitter clone();
}
步骤2.创建ISplitter的实现类BinarySplitter类和PictureSplitter类
public class BinarySplitter implements ISplitter {
@Override
public void split(String filePath, int fileNumber) {
System.out.println("this is BinarySplitter filePath::::::"+filePath+" fileNumber::::::"+fileNumber);
}
@Override
public ISplitter clone() {
BinarySplitter binarySplitter = new BinarySplitter();
return binarySplitter;
}
}
public class PictureSplitter implements ISplitter{
@Override
public void split(String filePath, int fileNumber) {
System.out.println("this is PictureSplitter filePath::::::"+filePath+" fileNumber::::::"+fileNumber);
}
@Override
public ISplitter clone() {
PictureSplitter pictureSplitter = new PictureSplitter();
return pictureSplitter;
}
}
步骤3.在SplitterMainForm类中使用
public class SplitterMainForm {
// 文件路径
private String filePath;
// 文件被分割的个数
private int fileNumber;
private ISplitter prototype;
public SplitterMainForm(String filePath,int fileNumber,ISplitter prototype) {
this.filePath = filePath;
this.fileNumber = fileNumber;
this.prototype = prototype;
}
// 点击按钮
public void onClick(String filePath,int fileNumber) {
// 此方式不对,prototype原型对象,不能直接用,原型对象是用于克隆的
// prototype.split(filePath,fileNumber);
ISplitter splitter = prototype.clone();
splitter.split(filePath,fileNumber);
}
}
注意:在SplitterMainForm代码中prototype原型对象不能直接调用split方法,因为原型对象就是用来被克隆的,
不能直接拿来用,否则违背这个模式的初衷。
既然这个模式与工厂模式那么那么的像,他俩的区别是什么?
原型设计模式主要解决的是某些结构复杂的对象的创建工作。
动机
在软件系统中,经常面临着“ 某些结构复杂的对象 ”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,
但是它们却拥有比较稳定一致的接口如何应对这种变化,如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,
从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
要点总结
Prototype 模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有“稳定的接口”
Prototype 模式对于“如何创建易变类的实体对象”采用“原型克隆”的方法来做,使得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象 — 所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方 clone
Prototype 模式中的 clone 方法可以利用某些框架中的序列化来实现深拷贝
深克隆和浅克隆
无论自己实现的克隆还是采用JAVA提供的克隆方法,都存在一个深克隆和浅克隆。
浅克隆:只负责克隆按值传递的数据(比如基本数据类型、String类型)
深克隆: 除了浅克隆的值外,还负责克隆引用类型的数据,基本上就是被克隆实例所有的属性数据都会被克隆出来。
自己实现深克隆
示例代码如下:
public class Client {
public static void main(String[] args) {
// 先创建原型实例
ProductOrder productOrder = new ProductOrder();
// 设置原型实例的值
Product product = new Product();
product.setName("产品1");
productOrder.setProduct(product);
System.out.println("这是第一次获取的对象实例="+productOrder);
// 通过克隆来获取新实例
ProductOrder productOrder2 = (ProductOrder) productOrder.cloneOrder();
// 修改它的值
productOrder2.getProduct().setName("产品2");
// 输出克隆出来的对象的值
System.out.println("输出克隆出来的实例="+productOrder2);
// 输出原型实例的值
System.out.println("再次输出原型实例="+productOrder);
}
}
public interface OrderApi {
public OrderApi cloneOrder();
}
public interface ProductPrototype {
ProductPrototype cloneProduct();
}
public class Product implements ProductPrototype{
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Product{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
@Override
public ProductPrototype cloneProduct() {
Product product = new Product();
product.setId(this.id);
product.setName(this.name);
return product;
}
}
public class ProductOrder implements OrderApi {
// 客户名称
private String customerName;
// 产品
private Product product;
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
@Override
public String toString() {
return "ProductOrder{" +
"customerName='" + customerName + '\'' +
", product=" + product +
'}';
}
@Override
public OrderApi cloneOrder() {
ProductOrder productOrder = new ProductOrder();
productOrder.setCustomerName(this.customerName);
productOrder.setProduct((Product)this.product.cloneProduct());
return productOrder;
}
}
JAVA实现深克隆
示例代码如下:
public class Client {
public static void main(String[] args) throws Exception{
// 先创建原型实例
ProductOrder productOrder = new ProductOrder();
// 设置原型实例的值
Product product = new Product();
product.setName("产品1");
productOrder.setProduct(product);
System.out.println("这是第一次获取的对象实例="+productOrder);
// 通过克隆来获取新实例
ProductOrder productOrder2 = (ProductOrder) productOrder.clone();
// 修改它的值
productOrder2.getProduct().setName("产品2");
// 输出克隆出来的对象的值
System.out.println("输出克隆出来的实例="+productOrder2);
// 输出原型实例的值
System.out.println("再次输出原型实例="+productOrder);
}
}
public class Product implements Cloneable {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Product{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class ProductOrder implements Cloneable {
// 客户名称
private String customerName;
// 产品
private Product product;
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
@Override
public String toString() {
return "ProductOrder{" +
"customerName='" + customerName + '\'' +
", product=" + product +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
ProductOrder obj = null;
obj = (ProductOrder)super.clone();
obj.setProduct((Product) this.product.clone());
return obj;
}
}
接口造接口
原型模式也可以用来解决"只知道接口而不知道实现的问题",使用原型模式,可以出现一种独特的"接口造接口"的现象
引题
现在有一个订单处理系统,里面有一个保存订单的业务功能,
需求:每当订单的预定产品数量超过1000的时候,就需要把你单拆成两份订单来保存。
如果拆成两份后还是超过1000,则继续拆分,直到每份产品预订数量不超过1000.
根据业务,目前的订单系统分成两种,一种是个人订单,一种是公司订单。
客户名称、产品对象(ID,Name),订购产品数量。
公司名称、 产品对象(ID, Name) ,订购产品数量。
不用模式的解决方案
步骤1.新建订单接口
public interface OrderApi {
/***
* 获取订单产品数量
* @return
*/
int getOrderProductNum();
/***
* 设置订单产品数量
* @param num
*/
void setOrderProductNum(int num);
}
步骤2.创建个人订单、企业订单
public class PersonalOrder implements OrderApi {
// 订购人姓名
private String customerName;
// 产品编号
private String productId;
// 订单产品数量
private int orderProductNum = 0;
@Override
public int getOrderProductNum() {
return orderProductNum;
}
@Override
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;
}
@Override
public String toString() {
return "PersonalOrder{" +
"customerName='" + customerName + '\'' +
", productId='" + productId + '\'' +
", orderProductNum=" + orderProductNum +
'}';
}
}
public class EnterpriseOrder implements OrderApi{
// 企业名称
private String enterpriseName;
// 产品编号
private String productId;
// 订单产品数量
private int orderProductNum = 0;
@Override
public int getOrderProductNum() {
return orderProductNum;
}
@Override
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;
}
@Override
public String toString() {
return "EnterpriseOrder{" +
"enterpriseName='" + enterpriseName + '\'' +
", productId='" + productId + '\'' +
", orderProductNum=" + orderProductNum +
'}';
}
}
步骤3.实现拆单业务逻辑类OrderBiz代码如下:
public class OrderBiz {
public void saveOrder(OrderApi order) {
// 1.判断当前的预定的产品的数量是否大于1000
while (order.getOrderProductNum() > 1000) {
// 2.如果大于,还需要继续拆分
// 2.1 再新建一份订单,跟传入的订单除了数量不一样外,其他都相同
OrderApi newOrder = null;
// 问题产生了,如何新建一份订单,现在OrderApi是一个接口,它根本就不知道现在订单具体类型,也不知道具体的订单实现,
// 所以无法创建出来新的订单对象,也就无法实现订单拆分。
}
}
}
上述代码产生了一个新问题如下:
如何新建一份订单,现在OrderApi是一个接口,它根本就不知道现在订单具体类型,也不知道具体的订单实现,所以无法创建出来新的订单对象,
也就无法实现订单拆分。
解决方案:
- 采用工厂方法模式,创建OrderApiFactory工厂接口,然后创建个人订单的具体工厂,和企业订单的具体工厂来生产订单对象。
- 使用instanceof方法判断
- 原型模式解决
如下代码是采用instanceof方式示意实现:
public class OrderBiz {
public void saveOrder(OrderApi order) {
// 1.判断当前的预定的产品的数量是否大于1000
while (order.getOrderProductNum() > 1000) {
// 2.如果大于,还需要继续拆分
// 2.1 再新建一份订单,跟传入的订单除了数量不一样外,其他都相同
OrderApi newOrder = null;
// 采用instanceof方式
if (order instanceof PersonalOrder) {
// 创建个人订单
PersonalOrder personalOrder = new PersonalOrder();
//进行赋值,省略......
newOrder = personalOrder;
} else if (order instanceof EnterpriseOrder) {
// 创建企业订单
EnterpriseOrder enterpriseOrder = new EnterpriseOrder();
//进行赋值,省略......
newOrder = enterpriseOrder;
}
// 进行拆分业务逻辑。省略......
}
}
}
instanceof方式有如下几个问题:
- 上述代码中OrderBiz类中依赖了具体的实现对象(PersonalOrder,EnterpriseOrder)导致OrderBiz不稳定。
- 难以扩展新的订单类型。假如现在要加入一个大客户订单类型,那么就需要修改OrderBiz类中代码,添加对新的订单类型的支持。
使用工厂方法模式解决
步骤1.OrderApiFactory工厂接口和OrderApi接口
public interface OrderApiFactory {
OrderApi createOrderApi();
}
public interface OrderApi {
/***
* 获取订单产品数量
* @return
*/
int getOrderProductNum();
/***
* 设置订单产品数量
* @param num
*/
void setOrderProductNum(int num);
}
步骤2.创建个人订单工厂和企业订单工厂
public class PersonalOrderFactory implements OrderApiFactory {
@Override
public OrderApi createOrderApi() {
return new PersonalOrder();
}
}
public class EnterpriseOrderFactory implements OrderApiFactory {
@Override
public OrderApi createOrderApi() {
return new EnterpriseOrder();
}
}
步骤3.创建个人订单对象和企业订单对象
public class PersonalOrder implements OrderApi {
// 订购人姓名
private String customerName;
// 产品编号
private String productId;
// 订单产品数量
private int orderProductNum = 0;
@Override
public int getOrderProductNum() {
return orderProductNum;
}
@Override
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;
}
@Override
public String toString() {
return "PersonalOrder{" +
"customerName='" + customerName + '\'' +
", productId='" + productId + '\'' +
", orderProductNum=" + orderProductNum +
'}';
}
}
public class EnterpriseOrder implements OrderApi {
// 企业名称
private String enterpriseName;
// 产品编号
private String productId;
// 订单产品数量
private int orderProductNum = 0;
@Override
public int getOrderProductNum() {
return orderProductNum;
}
@Override
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;
}
@Override
public String toString() {
return "EnterpriseOrder{" +
"enterpriseName='" + enterpriseName + '\'' +
", productId='" + productId + '\'' +
", orderProductNum=" + orderProductNum +
'}';
}
}
步骤4.实现订单处理业务逻辑类OrderBiz类
public class OrderBiz {
private OrderApiFactory orderApiFactory;
public OrderBiz(OrderApiFactory orderApiFactory) {
this.orderApiFactory = orderApiFactory;
}
public void saveOrder(OrderApi order) {
// 1.判断当前的预定的产品的数量是否大于1000
while (order.getOrderProductNum() > 1000) {
// 2.如果大于,还需要继续拆分
// 2.1 再新建一份订单,跟传入的订单除了数量不一样外,其他都相同
OrderApi newOrder = null;
// 问题产生了,如何新建一份订单,现在OrderApi是一个接口,它根本就不知道现在订单具体类型,也不知道具体的订单实现,
// 所以无法创建出来新的订单对象,也就无法实现订单拆分。
// 解决方案2个
// 1-可以使用工厂方法模式,创建OrderApiFactory工厂接口,然后创建个人订单的具体工厂,和企业订单的具体工厂
newOrder = orderApiFactory.createOrderApi();
// 进行拆分业务逻辑。省略......
}
}
}
原型模式解决
步骤1.创建OrderApi接口
public interface OrderApi {
/***
* 获取订单产品数量
* @return
*/
int getOrderProductNum();
/***
* 设置订单产品数量
* @param num
*/
void setOrderProductNum(int num);
// 克隆方法
OrderApi cloneOrder();
}
步骤2. 创建个人订单和企业订单
public class PersonalOrder implements OrderApi {
// 订购人姓名
private String customerName;
// 产品编号
private String productId;
// 订单产品数量
private int orderProductNum = 0;
@Override
public int getOrderProductNum() {
return orderProductNum;
}
@Override
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;
}
@Override
public String toString() {
return "PersonalOrder{" +
"customerName='" + customerName + '\'' +
", productId='" + productId + '\'' +
", orderProductNum=" + orderProductNum +
'}';
}
@Override
public OrderApi cloneOrder() {
return null;
}
}
public class EnterpriseOrder implements OrderApi {
// 企业名称
private String enterpriseName;
// 产品编号
private String productId;
// 订单产品数量
private int orderProductNum = 0;
@Override
public int getOrderProductNum() {
return orderProductNum;
}
@Override
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;
}
@Override
public String toString() {
return "EnterpriseOrder{" +
"enterpriseName='" + enterpriseName + '\'' +
", productId='" + productId + '\'' +
", orderProductNum=" + orderProductNum +
'}';
}
@Override
public OrderApi cloneOrder() {
return null;
}
}
如何实现克隆?
典型错误的示范:
@Override
public OrderApi cloneOrder() {
return this;
}
上述错误示范,每次克隆,客户端获取的其实都是同一个实例,都是指向同一个内存空间的,对克隆出来的对象实例修改会影响到原型对象实例。
正确的作法如下:
@Override
public OrderApi cloneOrder() {
PersonalOrder personalOrder = new PersonalOrder();
personalOrder.setProductId(this.productId);
personalOrder.setCustomerName(this.customerName);
personalOrder.setOrderProductNum(this.orderProductNum);
return personalOrder;
}
@Override
public OrderApi cloneOrder() {
EnterpriseOrder enterpriseOrder = new EnterpriseOrder();
enterpriseOrder.setProductId(this.productId);
enterpriseOrder.setEnterpriseName(this.enterpriseName);
enterpriseOrder.setOrderProductNum(this.orderProductNum);
return enterpriseOrder;
}
正式代码如下:
public class PersonalOrder implements OrderApi {
// 订购人姓名
private String customerName;
// 产品编号
private String productId;
// 订单产品数量
private int orderProductNum = 0;
@Override
public int getOrderProductNum() {
return orderProductNum;
}
@Override
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;
}
@Override
public String toString() {
return "PersonalOrder{" +
"customerName='" + customerName + '\'' +
", productId='" + productId + '\'' +
", orderProductNum=" + orderProductNum +
'}';
}
@Override
public OrderApi cloneOrder() {
PersonalOrder personalOrder = new PersonalOrder();
personalOrder.setProductId(this.productId);
personalOrder.setCustomerName(this.customerName);
personalOrder.setOrderProductNum(this.orderProductNum);
return personalOrder;
}
}
public class EnterpriseOrder implements OrderApi {
// 企业名称
private String enterpriseName;
// 产品编号
private String productId;
// 订单产品数量
private int orderProductNum = 0;
@Override
public int getOrderProductNum() {
return orderProductNum;
}
@Override
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;
}
@Override
public String toString() {
return "EnterpriseOrder{" +
"enterpriseName='" + enterpriseName + '\'' +
", productId='" + productId + '\'' +
", orderProductNum=" + orderProductNum +
'}';
}
@Override
public OrderApi cloneOrder() {
EnterpriseOrder enterpriseOrder = new EnterpriseOrder();
enterpriseOrder.setProductId(this.productId);
enterpriseOrder.setEnterpriseName(this.enterpriseName);
enterpriseOrder.setOrderProductNum(this.orderProductNum);
return enterpriseOrder;
}
}
步骤3.创建订单拆分业务逻辑处理OrderBiz类
public class OrderBiz {
public void saveOrder(OrderApi order) {
// 1.判断当前的预定的产品的数量是否大于1000
while (order.getOrderProductNum() > 1000) {
// 2.如果大于,还需要继续拆分
// 2.1 再新建一份订单,跟传入的订单除了数量不一样外,其他都相同
OrderApi newOrder = null;
// 问题产生了,如何新建一份订单,现在OrderApi是一个接口,它根本就不知道现在订单具体类型,也不知道具体的订单实现,
// 所以无法创建出来新的订单对象,也就无法实现订单拆分。
// 原型模式解决方案
newOrder = order.cloneOrder();
newOrder.setOrderProductNum(1000);
// 进行拆分业务逻辑。
// 2.2 原来的订单保留,把数量设置成减少1000
order.setOrderProductNum(order.getOrderProductNum()-1000);
// 然后是业务功能处理,省略,只是打印输出
System.out.println("拆分生成订单=="+newOrder);
}
// 3. 不超过,那就直接业务功能处理,省略,只是打印
System.out.println("订单=="+order);
}
}
真谛(找到变化,封装变化)
在上述订单原型模式的代码中,我们可以将代码分为两部分:稳定部分和变化部分。
稳定部分
稳定部分是指那些在整个系统中不太可能变化或者变化频率较低的部分。在原型模式的上下文中,稳定部分通常包括:
- OrderApi接口:OrderApi 接口定义了订单的基本行为,包括获取和设置产品数量以及克隆订单。
这个接口不太可能变化,因为它为不同类型的订单提供了统一的契约。
变化部分
变化部分是指那些随着需求变更或者业务逻辑的变化而可能频繁修改的部分。
在原型模式的上下文中,变化部分包括:
-
具体实现类:PersonalOrder 和 EnterpriseOrder 类是具体的订单实现类,它们包含了订单的具体业务逻辑。这些类可能会随着业务需求的变化而发生变化,比如添加新的属性、方法或者修改现有的逻辑。
-
克隆方法:虽然 cloneOrder 方法本身是稳定的,因为它遵循原型模式的基本要求,但具体的实现(即如何在不同类型的订单中实现克隆逻辑)可能会根据订单实现类的变化而变化。
-
客户端代码:客户端代码是直接使用原型模式的代码,它可能会随着业务逻辑的变化而发生变化。例如,客户端代码可能需要处理新类型的订单,或者需要调用新添加的订单方法。
总结
在原型模式中,变化部分则包括具体实现类和客户端代码,这些部分随着业务逻辑的变化而可能需要修改。
通过分离稳定部分和变化部分,我们可以更容易地维护和扩展系统,同时保持代码的稳定性和可重用性。