首页 技术 正文
技术 2022年11月20日
0 收藏 485 点赞 3,960 浏览 8971 个字

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模式结构图

设计模式10—设计模式之原型模式(Prototype)

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原型模式的调用顺序图

设计模式10—设计模式之原型模式(Prototype)

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接口

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,031
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,520
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,368
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,148
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,781
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,862