本文講PlaceOrder函數的實現(重點在業務邏輯層),讓我們來分別用不同的設計模式來實現吧:裝飾器模式、代理模式、命令模式、狀態模式、模版模式。
假設我們實現需求如下:
在PlaceOrder函數中需要做如下工作
1. 檢查權限,未登錄的不能遞交訂單
2. 計算稅
3. 記錄日志
好了,讓我們分別來實現吧....當然,是用不同的設計模式分別實現。
裝飾器模式實現
請看PlaceOrder函數方法體:
public bool PlaceOrder(OrderInfo order) { try { OrderService srv = new OrderService(); //核心業務類 TaxDecorator4OrderService tax = new TaxDecorator4OrderService(srv); //第一次裝飾 LogDecorator4OrderService log = new LogDecorator4OrderService(tax); //第二次裝飾 PermissionDecorator4OrderService permission = new PermissionDecorator4OrderService(log);//第三次裝飾 return permission.NewOrder(order); //這里調用的是最后一個裝飾器的方法(會鏈式反應,調用全部的關聯函數) } catch(Exception ex)//由於PermissionDecorator4OrderService中會拋出exception,因此用了try, catch { Console.WriteLine("exception: "+ex.Message); return false; } }
實現方法
1. 寫一個公共的接口,讓“核心業務類”、“裝飾類”都實現這個接口
2. “裝飾類”的構造器需要能注入這個公共接口
3. 然后像上面那樣拼裝飾鏈條
裝飾器代碼如下(核心代碼):
public class PermissionDecorator4OrderService:IOrderServiceComponent //上面所說的公共接口 { IOrderServiceComponent component; public PermissionDecorator4OrderService(IOrderServiceComponent component) //要提供注入的地方,此處是構造函數注入方式,你也可以根據情況使用其他注入方式 { this.component = component; } public bool NewOrder(OrderInfo order) { if (Identity.UserID <= 0) throw new Exception("Authorization exception"); return this.component.NewOrder(order); } }
代碼在本頁最下方有下載。
代理模式實現
請看PlaceOrder函數方法體:
public bool PlaceOrder(OrderInfo order) { try { OrderService srv = new OrderService(); //核心業務類(這個類就是要被控制訪問的類) OrderServiceAgency srvAgency = new OrderServiceAgency(); //代理類,外界的調用是通過它的,由它來轉發核心業務類的調用,意圖在於控制訪問 srvAgency.SetServiceComponent(srv); //此處為注入(方法注入方式),當然,你也可以使用構造器注入 return srvAgency.NewOrder(order); //看,此處調用的是代理的NewOrder } catch(Exception ex) { Console.WriteLine("exception: "+ex.Message); return false; } }
“核心業務類”、“代理類”,這2個類的外觀是相同的,最簡單的方式實現這個模式是
1. 為這2種類定義一個接口,這2種類都去實現
2. “代理類”除了要實現這個接口之外,還要持有這個接口的一個instance(這樣才能轉發調用)
3. “代理類”需要提供注入方式:構造函數、方法、屬性注入方式
“代理類”代碼如下(核心):
public class OrderServiceAgency : IOrderServiceComponent //實現同一個接口(外觀一致) { private IOrderServiceComponent component; //要持有一個instance public void SetServiceComponent(IOrderServiceComponent component) //注入這個instance { this.component = component; } public bool NewOrder(OrderInfo order) //“代理類”的接口方法中,可以自定義一些邏輯 { if (Identity.UserID <= 0) throw new Exception("Authorization exception"); order.Total += order.Total * (decimal)0.02;//模擬稅收 Console.WriteLine("loging..."); return this.component.NewOrder(order); //轉發請求到“業務類” } }
代碼在本頁最下方有下載。
命令模式
請看PlaceOrder函數方法體:
public bool PlaceOrder(OrderInfo order) { try { NewOrderCommandResult result=new NewOrderCommandResult(); //由於命令模式不像直接call、返回結果方式,因此寫了這個callback類,專門用來放執行結果 CheckPermissionCommand permissionCommand = new CheckPermissionCommand(new RealExecuters.PermissionService()); //檢查權限命令 CalculateTaxCommand calculateTaxCommand = new CalculateTaxCommand(new RealExecuters.TaxCalculator(), order); //計算稅命令 LogCommand logCommand = new LogCommand(new RealExecuters.LogService()); //記錄日志命令 NewOrderCommand newOrderCommand=new NewOrderCommand(new RealExecuters.OrderService(), order, result); //調用"核心業務類"命令 List<IPlaceOrderCommand> list = new List<IPlaceOrderCommand>(); //把這些命令都打包到一個list中 list.Add(permissionCommand); list.Add(calculateTaxCommand); list.Add(logCommand); list.Add(newOrderCommand); list.ForEach(t=>t.Execute()); //遍歷執行 return result.IsSuccess; //callback的執行結果 } catch(Exception ex) { Console.WriteLine("exception: "+ex.Message); return false; } }
原本的調用方式是直接call,然后目標對象返回結果,命令模式是在這步驟中間截取了一道,它通過增加一個command類來中轉對目標方法的調用,此時,只要保存這些command類就能打包命令的執行了。
核心代碼如下:
interface IPlaceOrderCommand //命令的抽象接口 { void Execute(); //就那么一個方法,Execute(), 而且是void和沒有參數的 } class LogCommand : IPlaceOrderCommand { private LogService logService; public LogCommand(LogService logService) //不同的Command需要注入相應的真正實現這個命令的類 { this.logService = logService; } public void Execute() { this.logService.Log("loging..."); //只是中轉調用 } }
class PermissionCheckCommand: IPlaceOrderCommand
class Log2Command:IPlaceOrderCommand
當這些XXXXXXXCommand被instance之后,就可以保存到Queue或者List,又或者序列化。。。。統一Execute(),而且此時執行的話,外觀已經一致了,並且沒有入參,很方便。
至於這個模式的callback result怎么寫,大家就看看demo代碼吧,這里不說了。
代碼在本頁最下方有下載。
狀態模式
請看PlaceOrder函數方法體:
public bool PlaceOrder(OrderInfo order) { try { bool isValidaUser = Identity.UserID > 0; IOrderServiceComponent component=null; if (isValidaUser) //根據條件狀態,去獲取不同的對象,執行不一樣的業務邏輯 component = new AuthorizaedNewOrderService(); else component = new UnAuthorizaedNewOrderService(); return component.NewOrder(order); } catch(Exception ex) { Console.WriteLine("exception: "+ex.Message); return false; } }
主要的原理是:把代碼中大塊的if/else中的代碼extract到其他class中,實現要點:
1. 有幾個分支,就寫幾個類,然后把if/else中的代碼重構過去
2. 這些新增的類,需要實現同一個接口
核心代碼如下:
public interface IOrderServiceComponent { bool NewOrder(OrderInfo order); } class UnAuthorizaedNewOrderService : IOrderServiceComponent { public bool NewOrder(OrderInfo order) { throw new Exception("Authorization exception"); } } class AuthorizaedNewOrderService : IOrderServiceComponent { public bool NewOrder(OrderInfo order) { order.Total += order.Total * (decimal)0.02;//模擬稅收 //validate entity //insert database Console.WriteLine("inserting database"); Console.WriteLine("loging..."); return true; } }
代碼在本頁最下方有下載。
模版模式
請看PlaceOrder函數方法體:
public bool PlaceOrder(OrderInfo order) { try { BaseOrderService srv = new AaronOrderService(); //BaseOrderService是定義的模版抽象類,里面定義了NewOrder函數的主要步驟邏輯 return srv.NewOrder(order); //AaronOrderService只是重寫/實現BaseOrderService的某些方法函數,達到部分自定義,全局固定的狀態 } catch(Exception ex) { Console.WriteLine("exception: "+ex.Message); return false; } }
這個模式在平台設計上也很有用,因為能夠做到全局固定、局部變化,簡而言之:該不變的就不變、該變的就要變,也有稱為熱點的。
核心代碼如下:
public abstract class BaseOrderService { public bool NewOrder(OrderInfo order) //這個就是骨架了,邏輯步驟是固定住的 { PermissionCheck(); TaxCalculate(order); bool success=CreateNewOrder(order); Log(); return success; } private void Log() { Console.WriteLine("loging..."); } protected abstract bool CreateNewOrder(OrderInfo order); //這個函數沒有實現,是需要去實現的 protected virtual void TaxCalculate(OrderInfo order) //這個函數實現了,但是定義成了virtual, 允許子類override { order.Total += order.Total * (decimal)0.02; } private void PermissionCheck() { if (Identity.UserID <= 0) throw new Exception("Authorization exception"); } }