前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式之策略者模式

设计模式之策略者模式

作者头像
Edison.Ma
发布2019-07-19 18:02:12
5250
发布2019-07-19 18:02:12
举报
文章被收录于专栏:DotNet Core圈圈DotNet Core圈圈

策略者模式简介

策略者模式定义一个算法接口,并由其实现类去实现,使得每一个算法都得到封装,并让他们可以相互替换。这是一种行为型模式。策略者模式降低了算法行为和环境角色的耦合度,使得算法可以独立发生变化。

策略者模式在现实世界的使用很多,比如互金场景中的优惠券模式,可以分为本金券,返现券,加息券,增收券等,每种卡券给予用户享受不同的权益,如果有一天增加了新的优惠券,也很容易扩展进去。由此可见,策略者模式使得业务线索更加清晰明了,每种业务线索场景彼此互不关联,互不影响。

同时,由于并不强耦合企业业务,所以当有一天企业业务扩大,并同时需要对卡券进行进一步的权益扩展的时候,修改起来也会很方便,当然某些可变数据是可以通过配置来解决的,这也进一步减少了代码的修改。 当然,我们也可以看到,根据特定的场景,充分运用其规则,并通过配合一些常规手段来进一步完善和稳定系统功能的时候,可以把设计模式的威力进一步发挥出来,切记不可拘泥于设计模式本身。

策略者模式UML类图

由UML类图可知策略者模式分为三个角色

Context:此处负责抽象策略类调度具体的算法策略,根据某些具体场景的不同,Context也可以有不同的实现。 Strategy:抽象算法策略类,所以具体策略者的父类,定义了一个抽象的方法,可以是接口也可以是抽象类,我一般使用抽象类,因为我需要对一些数据进行特殊的处理后再交给子类。 ConcreteStrategy:具体的算法策略,具体实现抽象的方法。

范例

以下范例,会使用前面所说的互金场景下的卡券,对于用户来说,就是购买产品时所使用的卡券能为自己带来多少收益,所以此处把【用】这个算法抽象出来,由每种卡券自己去实现响应的算法

策略算法抽象类:

代码语言:javascript
复制
   1:  public abstract class BaseCoupon
代码语言:javascript
复制
   2:  {
代码语言:javascript
复制
   3:     protected int productCircle;
代码语言:javascript
复制
   4:   
代码语言:javascript
复制
   5:     public BaseCoupon(Product product)
代码语言:javascript
复制
   6:     {
代码语言:javascript
复制
   7:         productCircle = (product.EndTime - product.StartTime).Days;
代码语言:javascript
复制
   8:     }
代码语言:javascript
复制
   9:   
代码语言:javascript
复制
  10:     public abstract decimal UseCoupon();
代码语言:javascript
复制
  11:  }

策略算法具体的四个卡券类

代码语言:javascript
复制
   1:  public class PrincipalCoupon : BaseCoupon
代码语言:javascript
复制
   2:  {
代码语言:javascript
复制
   3:      public PrincipalCoupon(Product product) : base(product)
代码语言:javascript
复制
   4:      {
代码语言:javascript
复制
   5:   
代码语言:javascript
复制
   6:      }
代码语言:javascript
复制
   7:   
代码语言:javascript
复制
   8:      /// <summary>
代码语言:javascript
复制
   9:      /// 使用本金券
代码语言:javascript
复制
  10:      /// </summary>
代码语言:javascript
复制
  11:      /// <returns></returns>
代码语言:javascript
复制
  12:      public override decimal UseCoupon()
代码语言:javascript
复制
  13:      {
代码语言:javascript
复制
  14:          Console.WriteLine($"此处使用的是本金券,产品周期{productCircle},经计算将返现1.2元");
代码语言:javascript
复制
  15:   
代码语言:javascript
复制
  16:          return 1.2M;
代码语言:javascript
复制
  17:      }
代码语言:javascript
复制
  18:  }
代码语言:javascript
复制
  19:   
代码语言:javascript
复制
  20:  public class CashBackCoupon : BaseCoupon
代码语言:javascript
复制
  21:  {
代码语言:javascript
复制
  22:      public CashBackCoupon(Product product) : base(product)
代码语言:javascript
复制
  23:      {
代码语言:javascript
复制
  24:   
代码语言:javascript
复制
  25:      }
代码语言:javascript
复制
  26:   
代码语言:javascript
复制
  27:      /// <summary>
代码语言:javascript
复制
  28:      /// 使用返现券
代码语言:javascript
复制
  29:      /// </summary>
代码语言:javascript
复制
  30:      public override decimal UseCoupon()
代码语言:javascript
复制
  31:      {
代码语言:javascript
复制
  32:          Console.WriteLine("此处使用的是返现券,产品周期{productCircle},经计算将返现12元");
代码语言:javascript
复制
  33:   
代码语言:javascript
复制
  34:          return 12M;
代码语言:javascript
复制
  35:      }
代码语言:javascript
复制
  36:  }
代码语言:javascript
复制
  37:   
代码语言:javascript
复制
  38:  public class IncreaseInterestCoupon : BaseCoupon
代码语言:javascript
复制
  39:  {
代码语言:javascript
复制
  40:      public IncreaseInterestCoupon(Product product) : base(product)
代码语言:javascript
复制
  41:      {
代码语言:javascript
复制
  42:   
代码语言:javascript
复制
  43:      }
代码语言:javascript
复制
  44:   
代码语言:javascript
复制
  45:      /// <summary>
代码语言:javascript
复制
  46:      /// 使用加息券
代码语言:javascript
复制
  47:      /// </summary>
代码语言:javascript
复制
  48:      public override decimal UseCoupon()
代码语言:javascript
复制
  49:      {
代码语言:javascript
复制
  50:          Console.WriteLine("此处使用的是加息券,产品周期{productCircle},经计算将返现1.5元");
代码语言:javascript
复制
  51:   
代码语言:javascript
复制
  52:          return 1.5M;
代码语言:javascript
复制
  53:      }
代码语言:javascript
复制
  54:  }
代码语言:javascript
复制
  55:   
代码语言:javascript
复制
  56:  public class IncreaseIncome : BaseCoupon
代码语言:javascript
复制
  57:  {
代码语言:javascript
复制
  58:      public IncreaseIncome(Product product) : base(product)
代码语言:javascript
复制
  59:      {
代码语言:javascript
复制
  60:   
代码语言:javascript
复制
  61:      }
代码语言:javascript
复制
  62:   
代码语言:javascript
复制
  63:      /// <summary>
代码语言:javascript
复制
  64:      /// 使用增收券
代码语言:javascript
复制
  65:      /// </summary>
代码语言:javascript
复制
  66:      public override decimal UseCoupon()
代码语言:javascript
复制
  67:      {
代码语言:javascript
复制
  68:          Console.WriteLine("此处使用的是增收券,产品周期{productCircle},经计算将返现5.5元");
代码语言:javascript
复制
  69:   
代码语言:javascript
复制
  70:          return 5.5M;
代码语言:javascript
复制
  71:      }
代码语言:javascript
复制
  72:  }

策略者上线文类,此处我提供了两种实现方式:

1、如果策略者上线文类比较简单,除了对象获取以外,没有其他特殊的使用,可以考虑类似于简单工厂的模式,毕竟,我们在开发卡券功能时,会提供相应的卡券类型枚举,此处可以借用一下

代码语言:javascript
复制
   1:  public class ConponUseContext
代码语言:javascript
复制
   2:  {
代码语言:javascript
复制
   3:      public static BaseCoupon GetCoupon(CouponType couponType, Product product)
代码语言:javascript
复制
   4:      {
代码语言:javascript
复制
   5:          switch (couponType)
代码语言:javascript
复制
   6:          {
代码语言:javascript
复制
   7:              case CouponType.PrincipalCoupon:
代码语言:javascript
复制
   8:                  return new CashBackCoupon(product);
代码语言:javascript
复制
   9:   
代码语言:javascript
复制
  10:              case CouponType.CashBackCoupon:
代码语言:javascript
复制
  11:                  return new CashBackCoupon(product);
代码语言:javascript
复制
  12:   
代码语言:javascript
复制
  13:              case CouponType.IncreaseInterestCoupon:
代码语言:javascript
复制
  14:                  return new IncreaseInterestCoupon(product);
代码语言:javascript
复制
  15:   
代码语言:javascript
复制
  16:              case CouponType.IncreaseIncome:
代码语言:javascript
复制
  17:                  return new IncreaseIncome(product);
代码语言:javascript
复制
  18:   
代码语言:javascript
复制
  19:              default:
代码语言:javascript
复制
  20:                  throw new Exception("未知的卡券类型");
代码语言:javascript
复制
  21:          }
代码语言:javascript
复制
  22:      }
代码语言:javascript
复制
  23:  }

调用方式

代码语言:javascript
复制
   1:  class Program
代码语言:javascript
复制
   2:      {
代码语言:javascript
复制
   3:          static void Main(string[] args)
代码语言:javascript
复制
   4:          {
代码语言:javascript
复制
   5:              Console.WriteLine("我用了本金券");
代码语言:javascript
复制
   6:   
代码语言:javascript
复制
   7:              decimal interest = ConponUseContext.GetCoupon(CouponType.PrincipalCoupon, new Product());
代码语言:javascript
复制
   8:   
代码语言:javascript
复制
   9:              Console.WriteLine($"该用户获得的收益是{interest}");
代码语言:javascript
复制
  10:          }
代码语言:javascript
复制
  11:      }

2、另外一种实现方式,就是采用注入方式,这种实现方式一般用于策略者上下文类功能比较多的情况

代码语言:javascript
复制
   1:  public class ConponUseContext
代码语言:javascript
复制
   2:  {
代码语言:javascript
复制
   3:      private BaseCoupon baseCoupon;
代码语言:javascript
复制
   4:      public ConponUseContext(BaseCoupon baseCoupon)
代码语言:javascript
复制
   5:      {
代码语言:javascript
复制
   6:          this.baseCoupon = baseCoupon;
代码语言:javascript
复制
   7:      }
代码语言:javascript
复制
   8:   
代码语言:javascript
复制
   9:      public decimal UseCoupon()
代码语言:javascript
复制
  10:      {
代码语言:javascript
复制
  11:          return this.baseCoupon.UseCoupon();
代码语言:javascript
复制
  12:      }
代码语言:javascript
复制
  13:  }

调用方式

代码语言:javascript
复制
   1:  class Program
代码语言:javascript
复制
   2:  {
代码语言:javascript
复制
   3:      static void Main(string[] args)
代码语言:javascript
复制
   4:      {
代码语言:javascript
复制
   5:          Console.WriteLine("我用了本金券");
代码语言:javascript
复制
   6:   
代码语言:javascript
复制
   7:          decimal interest = new ConponUseContext(new Product()).GetCoupon(CouponType.PrincipalCoupon);
代码语言:javascript
复制
   8:   
代码语言:javascript
复制
   9:          Console.WriteLine($"该用户获得的收益是{interest}");
代码语言:javascript
复制
  10:      }
代码语言:javascript
复制
  11:  }

策略者模式优缺点

优点:

  • 很好的体现了开闭原则,开发者可以在不变更其他具体算法的基础上新增新的策略类,即便是策略者的具体场景发生变化,并需要大规模修改时,也会很容易,因为独立的场景总会带来特定的思维模式,让开发者不会被其他场景所干扰,也就是所谓的关注点分离。
  • 避免了大量的if-else
  • 算法可以自由切换

缺点:

  • 有可能会产生大量的策略类,并且所有策略类都会对外暴露

策略者模式使用场景思考

其实这一块我并不想写,因为写了以后,会给人一种思维定势,但是此处还是需要多讨论一下什么场景下去使用策略者模式,我们可以做一个这样的思考,当代码中或者即将编写的功能需要配合大量的if-else,其中的代码会较为复杂,并且这些产生if-else出现了较强的逻辑上的关联,外界也根本不关注其中的具体实现,在加入一层抽象层后,会使得这些功能更加聚合,更加明确,这个时候,可以考虑使用策略者模式。需要提醒的时候,策略者模式关注的是对象的行为,如果关注对象本身,可以使用简单工厂。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet技术平台 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 策略者模式简介
  • 策略者模式UML类图
  • 范例
  • 策略者模式优缺点
  • 策略者模式使用场景思考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档