歡迎光臨
每天分享高質量文章

設計樣式之策略者樣式

策略者樣式簡介

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

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

同時,由於並不強耦合企業業務,所以當有一天企業業務擴大,並同時需要對卡券進行進一步的權益擴充套件的時候,修改起來也會很方便,當然某些可變資料是可以透過配置來解決的,這也進一步減少了程式碼的修改。
當然,我們也可以看到,根據特定的場景,充分運用其規則,並透過配合一些常規手段來進一步完善和穩定系統功能的時候,可以把設計樣式的威力進一步發揮出來,切記不可拘泥於設計樣式本身。

策略者樣式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 版的贊賞功能被關閉,可透過二維碼轉賬支援公眾號。


    已同步到看一看
    贊(0)

    分享創造快樂