9.1 场景问题
9.1.1 订单处理系统
考虑这样一个实际应用:订单处理系统。
现在有一个订单处理的系统,里面有个保存订单的业务功能,在这个业务功能里面,客户有这么一个需求:每当订单的预定产品数量超过1000的时候,就需要把订单拆成两份订单来保存,如果拆成两份订单后,还是超过1000,那就继续拆分,直到每份订单的预定产品数量不超过1000。至于为什么要拆分,原因是好进行订单的后续处理,后续是由人工来处理,每个人工工作小组的处理能力上限是1000。
根据业务,目前的订单类型被分成两种:一种是个人订单,一种是公司订单。现在想要实现一个通用的订单处理系统,也就是说,不管具体是什么类型的订单,都要能够正常的处理。
该怎么实现呢?
9.1.2 不用模式的解决方案
来分析上面要求实现的功能,有朋友会想,这很简单嘛,一共就一个功能,没什么困难的,真的是这样吗?先来尝试着实现看看。
(1)定义订单接口
首先,要想实现通用的订单处理,而不关心具体的订单类型,那么很明显,订单处理的对象应该面向一个订单的接口或是一个通用的订单对象来编程,这里就选用面向订单接口来处理吧,先把这个订单接口定义出来,示例代码如下:
/** * 订单的接口 */ public interface OrderApi { /** * 获取订单产品数量 * @return 订单中产品数量 */ public int getOrderProductNum(); /** * 设置订单产品数量 * @param num 订单产品数量 */ public void setOrderProductNum(int num); } |
(2)既然定义好了订单的接口,那么接下来把各种类型的订单实现出来,先看看个人的订单实现,示例代码如下:
/** * 个人订单对象 */ 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 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; } } |
有些朋友看到这里,可能会有这样的疑问:看上去上面两种类型的订单对象,仅仅是一个数据封装的对象,而且还有一些数据是相同的,为何不抽出一个父类来,把共同的数据定义在父类里面呢?
这里有两个考虑,一个是:这里仅仅是一个示意,实际情况远比这复杂,实际开发中不会仅仅是数据封装对象这么简单。另外一个是:为了后续示例的重点突出,这里要学习的是原型模式,因此就没有去抽取父类,以免对象层级过多,影响主题的展示。
(3)实现好了订单对象,接下来看看如何实现通用的订单处理,先把订单处理的对象大概定义出来,示例代码如下:
/** * 处理订单的业务对象 */ public class OrderBusiness { /** * 创建订单的方法 * @param order 订单的接口对象 */ public void saveOrder(OrderApi order){ //等待具体实现 } } |
现在的中心任务就是要来实现这个saveOrder的方法,传入的参数是一个订单的接口对象,这个方法要实现的功能:根据业务要求,当订单的预定产品数量超过1000的时候,就需要把订单拆成两份订单。
那好,来尝试着实现一下,因为预定的数量可能会很大,因此采用一个while循环来处理,直到拆分后订单的数量不超过1000,先把实现的思路写出来,示例代码如下:
public class OrderBusiness { public void saveOrder(OrderApi order){ //1:判断当前的预定产品数量是否大于1000 while(order.getOrderProductNum() > 1000){ //2:如果大于,还需要继续拆分 //2.1再新建一份订单,跟传入的订单除了数量不一样外,其它都相同 OrderApi newOrder = null;
} } } |
大家会发现,才刚写到第二步就写不下去了,为什么呢?因为现在判断需要拆分订单,也就是需要新建一个订单对象,可是订单处理对象面对的是订单的接口,它根本就不知道现在订单具体的类型,也不知道具体的订单实现,它无法创建出新的订单对象来,也就无法实现订单拆分的功能了。
(4)一个简单的解决办法
有朋友提供了这么一个解决的思路,他说:不就是在saveOrder方法里面不知道具体的类型,从而导致无法创建对象吗?很简单,使用instanceof来判断不就可以了,他还给出了他的实现示意,示意代码如下:
public class OrderBusiness { public void saveOrder(OrderApi order){ while(order.getOrderProductNum() > 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; } //然后进行拆分和其它业务功能处理,省略了 } } } |
好像能解决问题,对吧。那我们就来按照他提供的思路,把这个通用的订单处理对象实现出来,示例代码如下:
/** * 处理订单的业务对象 */ 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:不超过1000,那就直接业务功能处理,省略了,打印输出,看一下 System.out.println("订单=="+order); } } |
(5)写个客户端来测试一下,示例代码如下:
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); } } |
运行结果如下:
拆分生成订单==本个人订单的订购人是=张三,订购产品是=P0001,订购数量为=1000 拆分生成订单==本个人订单的订购人是=张三,订购产品是=P0001,订购数量为=1000 订单==本个人订单的订购人是=张三,订购产品是=P0001,订购数量为=925 |
根据订单中订购产品的数量,一份订单被拆分成了三份。
同样的,你还可以传入企业订单,看看是否能正常满足功能要求。
9.1.3 有何问题
看起来,上面的实现确实不难,好像也能够通用的进行订单处理,而不需要关心订单的类型和具体实现这样的功能。
仔细想想,真的没有关心订单的类型和具体实现吗?答案是“否定的”。
事实上,在实现订单处理的时候,上面的实现是按照订单的类型和具体实现来处理的,就是instanceof的那一段。有朋友可能会问,这样实现有何不可吗?
这样的实现有如下几个问题:
- 既然想要实现通用的订单处理,那么对于订单处理的实现对象,是不应该知道订单的具体实现的,更不应该依赖订单的具体实现。但是上面的实现中,很明显订单处理的对象依赖了订单的具体实现对象。
- 这种实现方式另外一个问题就是:难以扩展新的订单类型。假如现在要加入一个大客户专用订单的类型,那么就需要修改订单处理的对象,要在里面添加对新的订单类型的支持,这算哪门子的通用处理。
因此,上面的实现是不太好的,把上面的问题再抽象描述一下:已经有了某个对象实例后,如何能够快速简单地创建出更多的这种对象?
比如上面的问题,就是已经有了订单接口类型的对象实例,然后在方法中需要创建出更多的这种对象。怎么解决呢?
相关推荐
原型模式是设计模式中的一种,它是Java 23种经典设计模式之一,主要用来提高对象创建的效率。在原型模式中,我们通过复制或克隆一个已经存在的对象来创建新的对象,而不是通过构造函数来创建。这种方法尤其适用于当...
原型模式(Prototype Pattern)是软件设计模式中的一种结构型模式,它的主要目的是通过复制已有对象来创建新对象,从而减少创建新对象的成本。在原型模式中,类的实例化过程被替换为对已有实例的克隆操作,尤其适用...
原型模式是一种设计模式,主要目的是通过复制现有对象来创建新对象,从而简化创建过程并减少对象构造的开销。在Java中,实现原型模式通常涉及`Cloneable`接口和`clone()`方法。`Cloneable`接口是一个标记接口,类...
本文将深入探讨一种常见的设计模式——原型模式(Prototype Pattern),并结合具体的iOS应用场景进行解析。 原型模式是一种创建型设计模式,它的主要思想是通过复制已有对象来创建新对象,而不是通过构造函数来创建...
本文将深入探讨Android设计模式中的“原型模式”(Prototype Pattern),并结合提供的"prototype"压缩包中的示例代码进行解析。 原型模式是一种创建型设计模式,它的主要思想是通过复制已有对象来创建新对象,而...
原型模式(Prototype Pattern)是其中一种行为设计模式,主要用于对象创建。它通过复制已有对象来创建新对象,而不是通过传统的构造函数来创建。在Java中,原型模式可以有效地提高性能,特别是在创建复杂对象时。 #...
《精通Python设计模式1》这本书,就是一本深入探讨如何在Python中应用设计模式的实用指南。 首先,书中第一部分针对的是对象创建模式,这在任何软件开发过程中都是基础且至关重要的。对象创建模式让创建对象的过程...
原型模式(Prototype Pattern)是一种创建型设计模式,它允许我们通过复制现有的对象来创建新对象,而无需知道具体创建过程的细节。这种模式的核心在于,它提供了一种对象克隆的简便方法,使得对象的创建过程对用户...
1. **种类**:共有23种设计模式被记录在著名的《设计模式:可复用面向对象软件的基础》一书中,这些模式由Erich Gamma等四位作者提出,因此也被称为GOF设计模式。 2. **分类**:这23种模式可以根据其目的分为三大类...
C#设计模式详解 本文档详细介绍了23种C#设计模式,包括创建型、结构型和行为型三个大类。这些设计模式是.NET进阶必备的知识,通过学习和掌握这些设计模式,可以提高程序员的设计和编码能力。 创建型设计模式 1. ...
原型模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。在原型模式中,一个对象可以被克隆以创建新对象,而无需知道具体的创建细节。这种模式在需要重复创建相似对象时非常有用,避免了每次创建新对象时都...
【Spring AOP设计模式】是Spring框架中面向切面编程的重要组成部分,它允许开发者通过分离关注点来解耦代码,实现灵活的模块化设计。在本章中,我们将探讨17种设计模式在Spring AOP中的应用和实现,以及它们如何帮助...
设计模式是软件开发中的一种最佳实践,它提供了一套经过验证的、通用的解决方案,用于解决常见的编程问题。设计模式可以提升代码的可读性、可维护性和可扩展性,遵循一些基本原则,如单一职责原则、开闭原则、依赖...
设计模式 的分类 总体来说设计模式分为三大类: 创建型模式(5): 工厂方法模式 、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式(7): 适配器模式、装饰器模式、代理模式、外观模式、桥接模式、...
**原型设计模式(Prototype Pattern)**是一种创建型设计模式,它允许我们通过复制现有的对象来创建新对象,而不是通过构造函数来实例化新对象。在面向对象编程中,当我们需要频繁地创建具有相同或相似属性的对象时,...
设计模式是软件工程中的一种最佳实践,它是在特定情境下为了解决常见问题而形成的可重用解决方案。这个PDF文档“24种设计模式介绍与6大设计原则”旨在帮助开发者理解和应用这些模式,提高代码的可维护性和可扩展性。...
本篇文章将深入探讨“原型模式(Prototype)”这一经典的设计模式,它是面向对象设计的一个重要概念,尤其在C++编程中有着广泛的应用。 原型模式是一种创建型设计模式,它的核心思想是通过复制已有对象来创建新对象,...