博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式10---设计模式之原型模式(Prototype)
阅读量:4554 次
发布时间:2019-06-08

本文共 10400 字,大约阅读时间需要 34 分钟。

1.场景模式

考虑这样一个实际应用:订单处理系统

里面有一个保存订单的功能,当产品数量超过1000份以后,拆成两份订单,再超,那么就再拆。直到每份订单不超过1000为止,订单有两种,一个是个人订单,一个是公司订单,现在需要实现一个通用的订单处理系统。

2.场景模式代码(代码很简单,不一一讲解了)

package demo08.prototype.example1;/** * 订单的接口 */public interface OrderApi {	/**	 * 获取订单产品数量	 * 	 * @return 订单中产品数量	 */	public int getOrderProductNum();	/**	 * 设置订单产品数量	 * 	 * @param num	 *        订单产品数量	 */	public void setOrderProductNum(int num);}
package demo08.prototype.example1;/** * 个人订单对象 */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;	}}
package demo08.prototype.example1;/** * 企业订单对象 */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;	}}
package demo08.prototype.example1;/** * 处理订单的业务对象 */public class OrderBusiness {	/**	 * 创建订单的方法	 * 	 * @param order	 *        订单的接口对象	 */	public void saveOrder(OrderApi order) {		// 根据业务要求,当订单的预定的产品数量超过1000的时候,就需要把订单拆成两份订单		// 当然如果要做好,这里的1000应该做成常量,这么做是为了演示简单		// 1:判断当前的预定产品数量是否大于1000		while (order.getOrderProductNum() > 1000) {			// 2:如果大于,还需要继续拆分			// 2.1再新建一份订单,跟传入的订单除了数量不一样外,其他都相同			OrderApi newOrder = null;			if (order instanceof PersonalOrder) {				// 创建相应的新的订单对象				PersonalOrder p2 = new PersonalOrder();				// 然后进行赋值,但是产品数量为1000				PersonalOrder p1 = (PersonalOrder) order;				p2.setCustomerName(p1.getCustomerName());				p2.setProductId(p1.getProductId());				p2.setOrderProductNum(1000);				// 然后再设置给newOrder				newOrder = p2;			} else if (order instanceof EnterpriseOrder) {				// 创建相应的订单对象				EnterpriseOrder e2 = new EnterpriseOrder();				// 然后进行赋值,但是产品数量为1000				EnterpriseOrder e1 = (EnterpriseOrder) order;				e2.setEnterpriseName(e1.getEnterpriseName());				e2.setProductId(e1.getProductId());				e2.setOrderProductNum(1000);				// 然后再设置给newOrder				newOrder = e2;			}			// 2.2原来的订单保留,把数量设置成减少1000			order.setOrderProductNum(order.getOrderProductNum() - 1000);			// 然后是业务功能处理,省略了,打印输出,看一下			System.out.println("拆分生成订单==" + newOrder);		}		// 3:不超过,那就直接业务功能处理,省略了,打印输出,看一下		System.out.println("订单==" + order);	}//	public void saveOrder2(OrderApi order){//		int oldNum = order.getOrderProductNum();//		while(oldNum > 1000){//			//定义一个表示被拆分出来的新订单对象//			OrderApi newOrder = null;//			//			if(order instanceof PersonalOrder){//				//创建相应的订单对象//				PersonalOrder p2 = new PersonalOrder();//				//然后进行赋值等,省略了//				//然后再设置给newOrder//				newOrder = p2;//			}else if(order instanceof EnterpriseOrder){//				//创建相应的订单对象//				EnterpriseOrder e2 = new EnterpriseOrder();//				//然后进行赋值等,省略了//				//然后再设置给newOrder//				newOrder = e2;//			}			//			//然后进行拆分和其他业务功能处理,省略了//		}		//	}}
package demo08.prototype.example1;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);	}}

3.问题所在

仔细观察可以发现:实际上是关注订单的类型和具体实现的。证据如下:

// 1:判断当前的预定产品数量是否大于1000		while (order.getOrderProductNum() > 1000) {			// 2:如果大于,还需要继续拆分			// 2.1再新建一份订单,跟传入的订单除了数量不一样外,其他都相同			OrderApi newOrder = null;			if (order instanceof PersonalOrder) {				// 创建相应的新的订单对象				PersonalOrder p2 = new PersonalOrder();				// 然后进行赋值,但是产品数量为1000				PersonalOrder p1 = (PersonalOrder) order;				p2.setCustomerName(p1.getCustomerName());				p2.setProductId(p1.getProductId());				p2.setOrderProductNum(1000);				// 然后再设置给newOrder				newOrder = p2;			} else if (order instanceof EnterpriseOrder) {				// 创建相应的订单对象				EnterpriseOrder e2 = new EnterpriseOrder();				// 然后进行赋值,但是产品数量为1000				EnterpriseOrder e1 = (EnterpriseOrder) order;				e2.setEnterpriseName(e1.getEnterpriseName());				e2.setProductId(e1.getProductId());				e2.setOrderProductNum(1000);				// 然后再设置给newOrder				newOrder = e2;			}			// 2.2原来的订单保留,把数量设置成减少1000			order.setOrderProductNum(order.getOrderProductNum() - 1000);			// 然后是业务功能处理,省略了,打印输出,看一下			System.out.println("拆分生成订单==" + newOrder);		}

4.解决方案

是不是觉得上面代码很臃肿,很不爽,很不舒服,对头,这样说明你已经开始理解java设计模式的好处了。

解决它的方法就是使用原型模式

5.原型模式

5.1原型模式定义:

用原型实例指定创建对象的种类,并通过拷贝这些原型创新新的对象。

5.2如何解决

那么如何解决呢?大家有没有想过可以把这些复制的代码放在接口中,返回接口对象,这样就好像是接口创建了接口对象似的。而且,客户端就不用理解对象的一些操作了,知识最少化。

6.原型模式示例代码

6.1模式结构图

 

6.2声明一个克隆自身的接口

package demo08.prototype.example2;/** * 声明一个克隆自身的接口 */public interface Prototype {	/**	 * 克隆自身的方法	 * @return 一个从自身克隆出来的对象	 */	public Prototype clone();}

6.3具体的实现对象

package demo08.prototype.example2;/** * 克隆的具体实现对象 */public class ConcretePrototype1 implements Prototype {	public Prototype clone() {		//最简单的克隆,新建一个自身对象,由于没有属性,就不去复制值了		Prototype prototype = new ConcretePrototype1();		return prototype;	}}package demo08.prototype.example2;/** * 克隆的具体实现对象 */public class ConcretePrototype2 implements Prototype {	public Prototype clone() {		//最简单的克隆,新建一个自身对象,由于没有属性,就不去复制值了		Prototype prototype = new ConcretePrototype2();		return prototype;	}}

6.4客户端

package demo08.prototype.example2;/** * 使用原型的客户端 */public class Client {	/**	 * 持有需要使用的原型接口对象	 */	private Prototype prototype;	/**	 * 构造方法,传入需要使用的原型接口对象	 * 	 * @param prototype	 *        需要使用的原型接口对象	 */	public Client(Prototype prototype) {		this.prototype = prototype;	}	/**	 * 示意方法,执行某个功能操作	 */	public void operation() {		// 会需要创建原型接口的对象		Prototype newPrototype = prototype.clone();	}}

7.使用原型模式来重写示例代码

为了方便我就直接写改动的地方

7.1订单的接口

package demo08.prototype.example3;/** * 订单的接口,声明了可以克隆自身的方法 */public interface OrderApi {	/**	 * 获取订单产品数量	 * @return 订单中产品数量	 */	public int getOrderProductNum();	/**	 * 设置订单产品数量	 * @param num 订单产品数量	 */	public void setOrderProductNum(int num);	/**	 * 克隆方法	 * @return 订单原型的实例	 */	public OrderApi cloneOrder();}

7.2个人订单

package demo08.prototype.example3;/** * 个人订单对象 */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;	}}

7.3企业订单

package demo08.prototype.example3;/** * 企业订单对象 */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;	}}

7.4处理订单的业务对象

package demo08.prototype.example3;/** * 处理订单的业务对象 */public class OrderBusiness {	/**	 * 创建订单的方法	 * @param order 订单的接口对象	 */	public void saveOrder(OrderApi order){		//根据业务要求,当订单的预定的产品数量超过1000的时候,就需要把订单拆成两份订单		//当然如果要做好,这里的1000应该做成常量,这么做是为了演示简单				//1:判断当前的预定产品数量是否大于1000		while(order.getOrderProductNum() > 1000){			//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);			}}

这样是不是简单清爽了很多呢?

8.原型模式思考

8.1原型模式的讲解

通过克隆创建新的对象实例

为克隆出来的对象复制原型实例属性的值,不是new而是类似new

8.2原型模式的调用顺序图

 

8.3浅度克隆和深度克隆

浅度克隆:只负责按值传递的数据

深度克隆:除了浅度克隆复制的数据外,还要负责克隆引用类型的数据,而且,引用类型还要递归克隆
java也有克隆方法,这里就不说了。
体现深度克隆的代码如下(稍微更改一下就行了)
public OrderApi cloneOrder() {
//创建一个新的订单,然后把本实例的数据复制过去
PersonalOrder order = new PersonalOrder();
order.setCustomerName(this.customerName);
order.setOrderProductNum(this.orderProductNum);
//对于对象类型的数据,深度克隆的时候需要继续调用这个对象的克隆方法
order.setProduct((Product)this.product.cloneProduct());
return order;
}

8.4原型模式本质

克隆生成对象

8.5原型模式优缺点

优点:对客户端隐藏具体的实现类型,运行时动态改变具体的实现类型

缺点:每个原型的子类都要实现clone接口

转载于:https://www.cnblogs.com/dyllove98/p/3163077.html

你可能感兴趣的文章
Spring基础2
查看>>
【灵异短篇】这个夜晚有点凉
查看>>
一点小问题
查看>>
pytest 10 skip跳过测试用例
查看>>
MVC身份验证及权限管理
查看>>
It was not possible to find any compatible framework version
查看>>
gulp与webpack的区别
查看>>
offset--BUG
查看>>
CSS选择器
查看>>
POJ_3667 线段树+lazy (线段树经典题)
查看>>
Android获取图片资源的4种方式
查看>>
找工作---操作系统常考知识点总结【PB】
查看>>
解决ionic <ion-nav> rootParams获取不到参数
查看>>
Python学习02 列表 List
查看>>
[DOM Event Learning] Section 3 jQuery事件处理基础 on(), off()和one()方法使用
查看>>
python爬虫-淘宝商品密码(图文教程附源码)
查看>>
centos6.3下如何搭建LAMP环境
查看>>
C#的一些基础内容
查看>>
nodejs概述
查看>>
H3C PAP验证配置示例
查看>>