设计模式10---设计模式之原型模式(Prototype)
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接口
设计模式10---设计模式之原型模式(Prototype)的更多相关文章
- 设计模式(四)原型模式Prototype(创建型)
		设计模式(四)原型模式Prototype(创建型) 1. 概述 我们都知道,创建型模式一般是用来创建一个新的对象,然后我们使用这个对象完成一些对象的操作,我们通过原型模式可以快速的创建一个对象 ... 
- 跟着实例学习设计模式(7)-原型模式prototype(创建型)
		原型模式是创建型模式. 设计意图:用原型实例指定创建对象的类型,并通过拷贝这个原型来创建新的对象. 我们使用构建简历的样例的类图来说明原型模式. 类图: 原型模式主要用于对象的复制.它的核心是就是类图 ... 
- 设计模式(五)原型模式 Prototype
		原型模式: 原型模式,是指基于一个已经给定的对象,通过拷贝的方式,创建一个新的对象,这个给定对象,就是“原型”. 在 Java 中,原型模式体现为 Object 的 clone() 方法. 所有类都可 ... 
- 设计模式学习系列6 原型模式(prototype)
		原型模式(prototype)用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.允许一个对象再创建另外一个新对象的时候根本无需知道任何创建细节,只需要请求圆形对象的copy函数皆可. 1 ... 
- Java设计模式(4)原型模式(Prototype模式)
		Prototype模式定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是: ... 
- 小菜学习设计模式(四)—原型(Prototype)模式
		前言 设计模式目录: 小菜学习设计模式(一)—模板方法(Template)模式 小菜学习设计模式(二)—单例(Singleton)模式 小菜学习设计模式(三)—工厂方法(Factory Method) ... 
- Net设计模式实例之原型模式( Prototype Pattern)
		一.原型模式简介(Brief Introduction) 原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. Specify the kin ... 
- 乐在其中设计模式(C#) - 原型模式(Prototype Pattern)
		原文:乐在其中设计模式(C#) - 原型模式(Prototype Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 原型模式(Prototype Pattern) 作者:weba ... 
- PHP设计模式 原型模式(Prototype)
		定义 和工厂模式类似,用来创建对象.但实现机制不同,原型模式是先创建一个对象,采用clone的方式进行新对象的创建. 场景 大对象的创建. 优点 1.可以在运行时刻增加和删除产品 2.可以改变值或结构 ... 
- 设计模式系列之原型模式(Prototype Pattern)——对象的克隆
		说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ... 
随机推荐
- 【Android UI设计与开发】第17期:滑动菜单栏(二)开源项目SlidingMenu的示例
			通过上一篇文章的讲解,相信大家对于开源项目SlidingMenu都有了一个比较初步的了解(不了解的可以参考上 一篇文章),那么从这一章开始,博主将会以SlidingMenu为重心,给大家带来非常丰富的 ... 
- Java调用cmd命令 打开一个站点
			使用Java程序打开一个站点 近期做了个东西使用SWT技术在一个client程序 须要升级时在提示升级 点击窗口上的一个连接 打开下载网页 花费了我非常长时间 用到了把它记录下来 怕是忘记,须要时能 ... 
- CentOS下yum使用代理的设置
			export后好像没用? 问题描述: CentOS yum时出现“Could not retrieve mirrorlist http://mirrorlist.centos.org/?release ... 
- Windows查看进程taskList,终止进程tskill
			TaskList: 列出当前所有运行进程. 使用方法:在命令提示符中输入tasklist 然后回车,会看到类似下面的列表: 映像名称 ... 
- ThinkPHP框架视图详细介绍 View 视图--模板(九)
			原文:ThinkPHP框架视图详细介绍 View 视图--模板(九) 视图也是ThinkPHP使用的核心部分: 一.模板的使用 a.规则 模板文件夹下[TPL]/[分组文件夹/][模板主题文件夹/]和 ... 
- 从零开始学C++之从C到C++(二):引用、内联函数inline、四种类型转换运算符
			一.引用 (1).引用是给一个变量起别名 定义引用的一般格式:类型 &引用名 = 变量名: 例如:int a=1; int &b=a;// b是a的别名,因此a和b是同一个单元 注 ... 
- DL动态载入框架技术文档
			DL动态载入框架技术文档 DL技术交流群:215680213 1. Android apk动态载入机制的研究 2. Android apk动态载入机制的研究(二):资源载入和activity生命周期管 ... 
- leetcode第一刷_Unique Paths
			从左上到右下,仅仅能向右或向下,问一共同拥有多少种走法. 这个问题当然能够用递归和dp来做,递归的问题是非常可能会超时,dp的问题是须要额外空间. 事实上没有其它限制条件的话,这个问题有个非常easy ... 
- 一二三(The Seventh Hunan Collegiate Programming Contest)
			一二三 你弟弟刚刚学会写英语的一(one).二(two)和三(three).他在纸上写了好些一二三,可惜有些字母写错了.已知每个单词最多有一个字母写错了(单词长度肯定不会错),你能认出他写的啥吗? 输 ... 
- Linux 命令学习之dpkg命令详解
			dpkg是一个Debian的一个命令行工具,它可以用来安装.删除.构建和管理Debian的软件包. 下面是它的一些命令解释: 1)安装软件 命令行:dpkg -i <.deb file name ... 
