策略者模式定义一个算法接口,并由其实现类去实现,使得每一个算法都得到封装,并让他们可以相互替换。这是一种行为型模式。策略者模式降低了算法行为和环境角色的耦合度,使得算法可以独立发生变化。
策略者模式在现实世界的使用很多,比如互金场景中的优惠券模式,可以分为本金券,返现券,加息券,增收券等,每种卡券给予用户享受不同的权益,如果有一天增加了新的优惠券,也很容易扩展进去。由此可见,策略者模式使得业务线索更加清晰明了,每种业务线索场景彼此互不关联,互不影响。
同时,由于并不强耦合企业业务,所以当有一天企业业务扩大,并同时需要对卡券进行进一步的权益扩展的时候,修改起来也会很方便,当然某些可变数据是可以通过配置来解决的,这也进一步减少了代码的修改。 当然,我们也可以看到,根据特定的场景,充分运用其规则,并通过配合一些常规手段来进一步完善和稳定系统功能的时候,可以把设计模式的威力进一步发挥出来,切记不可拘泥于设计模式本身。
由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出现了较强的逻辑上的关联,外界也根本不关注其中的具体实现,在加入一层抽象层后,会使得这些功能更加聚合,更加明确,这个时候,可以考虑使用策略者模式。需要提醒的时候,策略者模式关注的是对象的行为,如果关注对象本身,可以使用简单工厂。