天天看點

設計模式之政策者模式

政策者模式簡介

政策者模式定義一個算法接口,并由其實作類去實作,使得每一個算法都得到封裝,并讓他們可以互相替換。這是一種行為型模式。政策者模式降低了算法行為和環境角色的耦合度,使得算法可以獨立發生變化。

政策者模式在現實世界的使用很多,比如互金場景中的優惠券模式,可以分為本金券,返現券,加息券,增收券等,每種卡券給予使用者享受不同的權益,如果有一天增加了新的優惠券,也很容易擴充進去。由此可見,政策者模式使得業務線索更加清晰明了,每種業務線索場景彼此互不關聯,互不影響。

同時,由于并不強耦合企業業務,是以當有一天企業業務擴大,并同時需要對卡券進行進一步的權益擴充的時候,修改起來也會很友善,當然某些可變資料是可以通過配置來解決的,這也進一步減少了代碼的修改。

當然,我們也可以看到,根據特定的場景,充分運用其規則,并通過配合一些正常手段來進一步完善和穩定系統功能的時候,可以把設計模式的威力進一步發揮出來,切記不可拘泥于設計模式本身。

政策者模式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:      /// <summary>      
9:      /// 使用本金券      
10:      /// </summary>      
11:      /// <returns></returns>      
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:      /// <summary>      
28:      /// 使用返現券      
29:      /// </summary>      
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:      /// <summary>      
46:      /// 使用加息券      
47:      /// </summary>      
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:      /// <summary>      
64:      /// 使用增收券      
65:      /// </summary>      
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出現了較強的邏輯上的關聯,外界也根本不關注其中的具體實作,在加入一層抽象層後,會使得這些功能更加聚合,更加明确,這個時候,可以考慮使用政策者模式。需要提醒的時候,政策者模式關注的是對象的行為,如果關注對象本身,可以使用簡單工廠。
  • 以上為本篇文章的主要内容,希望大家多提意見,如果喜歡記得點個推薦哦

    作者:

    艾心

    出處:

    https://www.cnblogs.com/edison0621/

    本文版權歸作者和部落格園共有,歡迎轉載,轉載時保留原作者和文章位址即可。

  • 繼續閱讀