日前有一位朋友再次問到委派(delegate)的觀念問題(他要設計網站購物車與優惠折扣方面的物件),是否能舉一個更實際的例子,來進行新舊委派的寫法。當然,我不可能真的跳下去幫他寫真實的類別元件,我只能夠藉由一些其實毫無作用的程式碼來演繹一下委派的寫法,意思到就好了啦!
我們的任務如下:
產品類別: 產品名稱 產品價格 購物車類別: 產品集合屬性 計算總金額方法 主控台: 將挑選好的產品傳入,請購物車類別幫忙計算金額
有了上面的初步規劃後,我們還要知道一件事情,就是購物車類別的計算總金額方法中,千萬不可以把「折扣優惠判斷式」寫在購物車類別裡面,因為老闆會常常改促銷優惠條件,那你的購物車類別就得常常更迭了,所以不如拉到外面去做吧!
觀察的重點可以擺在Cart類別中的public delegate int Formula(int iSum),這一行就是很經典的委派簽名。另外一個觀察的點就是Calculator(Formula oFormula)這個方法的引數掛上了Formula委派簽名,讓方法內的程式碼可以開始引用這個委派,其他的寫法直接看程式碼吧!
class Program { static void Main(string[] args) { //新增購物車,並把商品放入 Cart oCart = new Cart() { oProducts = new List<Product> { new Product() {cName="AAA", iPrice= 200}, new Product() {cName="BBB", iPrice= 500}, new Product() {cName="CCC", iPrice= 800}, new Product() {cName="DDD", iPrice= 900} } }; //將促銷公式,指派給Discount int iResult = oCart.Calculator(Discount); //輸出金額 WriteLine($"總金額:{iResult}"); ReadKey(); } //促銷公式 public static int Discount(int iSum) { //超過一千元,單純減價100元 if (iSum > 1000) { return iSum - 100; } else { return iSum; } } } //產品類別 public class Product { public string cName { get; set; } public int iPrice { get; set; } } //購物車類別 public class Cart { public System.Collections.Generic.List<Product> oProducts; //(委派簽名)優惠促銷公式一定會常改,因此委派出去 public delegate int Formula(int iSum); public int Calculator(Formula oFormula) { //計算總金額(或者這邊應該實作檢查庫存) int iSum = oProducts.Sum(x => x.iPrice); //委派給外部公式後,返回折扣後金額 return oFormula(iSum); } }
最後執行的結果,本來的總金額是2400元,但是因為超過了1000元所以優惠100元,變成2300元。執行結果有正確的繞出去委派交付運行。
新式委派說穿了,就是捨棄掉傳統的delegate寫法,改採用微軟預先做好的Func委派簽名(匿名委派),然後調用端的方式拋棄傳統的具名委派方法實作,改採用潮到出水匿名方法來運作,其他的就直接看程式碼吧。
class Program { static void Main(string[] args) { //新增購物車,並把商品放入 Cart oCart = new Cart() { oProducts = new List<Product> { new Product() {cName="AAA", iPrice= 200}, new Product() {cName="BBB", iPrice= 500}, new Product() {cName="CCC", iPrice= 800}, new Product() {cName="DDD", iPrice= 900} } }; //將促銷公式委派給匿名型別 int iResult = oCart.Calculator((oProducts) => { //自己計算總金額 int iSum = oProducts.Sum(x => x.iPrice); //自己計算促銷公式 if (iSum > 1000) { return iSum - 100; } else { return iSum; } }); //輸出金額 WriteLine($"總金額:{iResult}"); ReadKey(); } } //產品類別 public class Product { public string cName { get; set; } public int iPrice { get; set; } } //購物車類別 public class Cart { public System.Collections.Generic.List<Product> oProducts; //這個方法基本上只剩下出一支嘴的能力,因為把所有的東西都丟給外面的委派方法了 public int Calculator(System.Func<System.Collections.Generic.List<Product>, int> oFormula) { //這邊能做的事情大概類似檢查資料庫庫存,並把已經沒貨的商品從清單拿掉,然後拋回去給呼叫者自己計算 //委派給外部公式後,返回折扣後金額 return oFormula(oProducts); } }
計算結果金額依然為2300元,只是採用新式委派的寫法後,程式碼變得更精簡。委派在大型專案中類別的切割有其優點,但從上面的程式碼中大家也可以看到一些詭異的點,就是有些類別基本上已經完全不做事了。類別的分工切割本身是一門藝術,如何讓類別方法保持單一責任(Single Responsibility Principle,SRP)這就依賴大家的智慧嘍。
相關連結:
C# delegate Func(T) AnonymousFunction Lambda SingleResponsibilityPrinciple S.O.L.I.D