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的那一段。有朋友可能會問,這樣實現有何不可嗎?
這樣的實現有如下幾個問題:
- 既然想要實現通用的訂單處理,那麼對於訂單處理的實現對象,是不應該知道訂單的具體實現的,更不應該依賴訂單的具體實現。但是上面的實現中,很明顯訂單處理的對象依賴了訂單的具體實現對象。
- 這種實現方式另外一個問題就是:難以擴展新的訂單類型。假如現在要加入一個大客戶專用訂單的類型,那麼就需要修改訂單處理的對象,要在裏面添加對新的訂單類型的支持,這算哪門子的通用處理。
因此,上面的實現是不太好的,把上面的問題再抽象描述一下:已經有了某個對象實例後,如何能夠快速簡單地創建出更多的這種對象?
比如上面的問題,就是已經有了訂單接口類型的對象實例,然後在方法中需要創建出更多的這種對象。怎麼解決呢?