策略者樣式簡介
策略者樣式定義一個演演算法介面,並由其實現類去實現,使得每一個演演算法都得到封裝,並讓他們可以相互替換。這是一種行為型樣式。策略者樣式降低了演演算法行為和環境角色的耦合度,使得演演算法可以獨立發生變化。
策略者樣式在現實世界的使用很多,比如互金場景中的優惠券樣式,可以分為本金券,返現券,加息券,增收券等,每種卡券給予使用者享受不同的權益,如果有一天增加了新的優惠券,也很容易擴充套件進去。由此可見,策略者樣式使得業務線索更加清晰明瞭,每種業務線索場景彼此互不關聯,互不影響。
同時,由於並不強耦合企業業務,所以當有一天企業業務擴大,並同時需要對卡券進行進一步的權益擴充套件的時候,修改起來也會很方便,當然某些可變資料是可以透過配置來解決的,這也進一步減少了程式碼的修改。
當然,我們也可以看到,根據特定的場景,充分運用其規則,並透過配合一些常規手段來進一步完善和穩定系統功能的時候,可以把設計樣式的威力進一步發揮出來,切記不可拘泥於設計樣式本身。
策略者樣式UML類圖
由UML類圖可知策略者樣式分為三個角色
Context:此處負責抽象策略類排程具體的演演算法策略,根據某些具體場景的不同,Context也可以有不同的實現。
Strategy:抽象演演算法策略類,所以具體策略者的父類,定義了一個抽象的方法,可以是介面也可以是抽象類,我一般使用抽象類,因為我需要對一些資料進行特殊的處理後再交給子類。
ConcreteStrategy:具體的演演算法策略,具體實現抽象的方法。
範例
以下範例,會使用前面所說的互金場景下的卡券,對於使用者來說,就是購買產品時所使用的卡券能為自己帶來多少收益,所以此處把【用】這個演演算法抽象出來,由每種卡券自己去實現響應的演演算法
策略演演算法抽象類:
1: public abstract class BaseCoupon
2: {
3: protected int productCircle;
4:
5: public BaseCoupon(Product product)
6: {
7: productCircle = (product.EndTime - product.StartTime).Days;
8: }
9:
10: public abstract decimal UseCoupon();
11: }
策略演演算法具體的四個卡券類
1: public class PrincipalCoupon : BaseCoupon
2: {
3: public PrincipalCoupon(Product product) : base(product)
4: {
5:
6: }
7:
8: ///
9: /// 使用本金券
10: ///
11: ///
12: public override decimal UseCoupon()
13: {
14: Console.WriteLine($"此處使用的是本金券,產品週期{productCircle},經計算將返現1.2元");
15:
16: return 1.2M;
17: }
18: }
19:
20: public class CashBackCoupon : BaseCoupon
21: {
22: public CashBackCoupon(Product product) : base(product)
23: {
24:
25: }
26:
27: ///
28: /// 使用返現券
29: ///
30: public override decimal UseCoupon()
31: {
32: Console.WriteLine("此處使用的是返現券,產品週期{productCircle},經計算將返現12元");
33:
34: return 12M;
35: }
36: }
37:
38: public class IncreaseInterestCoupon : BaseCoupon
39: {
40: public IncreaseInterestCoupon(Product product) : base(product)
41: {
42:
43: }
44:
45: ///
46: /// 使用加息券
47: ///
48: public override decimal UseCoupon()
49: {
50: Console.WriteLine("此處使用的是加息券,產品週期{productCircle},經計算將返現1.5元");
51:
52: return 1.5M;
53: }
54: }
55:
56: public class IncreaseIncome : BaseCoupon
57: {
58: public IncreaseIncome(Product product) : base(product)
59: {
60:
61: }
62:
63: ///
64: /// 使用增收券
65: ///
66: public override decimal UseCoupon()
67: {
68: Console.WriteLine("此處使用的是增收券,產品週期{productCircle},經計算將返現5.5元");
69:
70: return 5.5M;
71: }
72: }
策略者上線文類,此處我提供了兩種實現方式:
1、如果策略者上線文類比較簡單,除了物件獲取以外,沒有其他特殊的使用,可以考慮類似於簡單工廠的樣式,畢竟,我們在開發卡券功能時,會提供相應的卡券型別列舉,此處可以借用一下
1: public class ConponUseContext
2: {
3: public static BaseCoupon GetCoupon(CouponType couponType, Product product)
4: {
5: switch (couponType)
6: {
7: case CouponType.PrincipalCoupon:
8: return new CashBackCoupon(product);
9:
10: case CouponType.CashBackCoupon:
11: return new CashBackCoupon(product);
12:
13: case CouponType.IncreaseInterestCoupon:
14: return new IncreaseInterestCoupon(product);
15:
16: case CouponType.IncreaseIncome:
17: return new IncreaseIncome(product);
18:
19: default:
20: throw new Exception("未知的卡券型別");
21: }
22: }
23: }
呼叫方式
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: Console.WriteLine("我用了本金券");
6:
7: decimal interest = ConponUseContext.GetCoupon(CouponType.PrincipalCoupon, new Product());
8:
9: Console.WriteLine($"該使用者獲得的收益是{interest}");
10: }
11: }
2、另外一種實現方式,就是採用註入方式,這種實現方式一般用於策略者背景關係類功能比較多的情況
1: public class ConponUseContext
2: {
3: private BaseCoupon baseCoupon;
4: public ConponUseContext(BaseCoupon baseCoupon)
5: {
6: this.baseCoupon = baseCoupon;
7: }
8:
9: public decimal UseCoupon()
10: {
11: return this.baseCoupon.UseCoupon();
12: }
13: }
呼叫方式
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: Console.WriteLine("我用了本金券");
6:
7: decimal interest = new ConponUseContext(new Product()).GetCoupon(CouponType.PrincipalCoupon);
8:
9: Console.WriteLine($"該使用者獲得的收益是{interest}");
10: }
11: }
策略者樣式優缺點
優點:
- 很好的體現了開閉原則,開發者可以在不變更其他具體演演算法的基礎上新增新的策略類,即便是策略者的具體場景發生變化,並需要大規模修改時,也會很容易,因為獨立的場景總會帶來特定的思維樣式,讓開發者不會被其他場景所幹擾,也就是所謂的關註點分離。
- 避免了大量的if-else
- 演演算法可以自由切換
缺點:
- 有可能會產生大量的策略類,並且所有策略類都會對外暴露
策略者樣式使用場景思考
其實這一塊我並不想寫,因為寫了以後,會給人一種思維定勢,但是此處還是需要多討論一下什麼場景下去使用策略者樣式,我們可以做一個這樣的思考,當程式碼中或者即將編寫的功能需要配合大量的if-else,其中的程式碼會較為複雜,並且這些產生if-else出現了較強的邏輯上的關聯,外界也根本不關註其中的具體實現,在加入一層抽象層後,會使得這些功能更加聚合,更加明確,這個時候,可以考慮使用策略者樣式。需要提醒的時候,策略者樣式關註的是物件的行為,如果關註物件本身,可以使用簡單工廠。
受蘋果公司新規定影響,微信 iOS 版的贊賞功能被關閉,可透過二維碼轉賬支援公眾號。
朋友會在“發現-看一看”看到你“在看”的內容