勇哥注:
策略模式和状态模式是两个亲兄弟,类图很像。
策略模式是封装了一系列算法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同。就像是同样是亿万富豪,马云是靠卖东西,王思聪是靠继承。
状态模式的每种状态做的事,可以完全不同。
按类图编写的代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication20 { class Program { static void Main(string[] args) { Context c = new Context(new ConcreteStrategyB()); c.ContextInterface(); c = new Context(new ConcreteStrategyA()); c.ContextInterface(); Console.ReadKey(); } } public abstract class Strategy { public abstract void Algorithmlnterface(); } public class ConcreteStrategyA : Strategy { public override void Algorithmlnterface() { Console.WriteLine(@"算法A"); } } public class ConcreteStrategyB : Strategy { public override void Algorithmlnterface() { Console.WriteLine(@"算法B"); } } public class ConcreteStrategyC : Strategy { public override void Algorithmlnterface() { Console.WriteLine(@"算法C"); } } public class Context { private Strategy strategy; public Context(Strategy s) { this.strategy = s; } public void ContextInterface() { strategy.Algorithmlnterface(); } } }
何时使用策略模式
阿里开发规约-编程规约-控制语句-第六条 :
超过 3 层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现。
相信大家都见过这种代码:
if (conditionA) { 逻辑1 } else if (conditionB) { 逻辑2 } else if (conditionC) { 逻辑3 } else { 逻辑4 }
这种代码虽然写起来简单,但是很明显违反了面向对象的 2 个基本原则:
单一职责原则(一个类应该只有一个发生变化的原因):因为之后修改任何一个逻辑,当前类都会被修改
开闭原则(对扩展开放,对修改关闭):如果此时需要添加(删除)某个逻辑,那么不可避免的要修改原来的代码
因为违反了以上两个原则,尤其是当 if-else 块中的代码量比较大时,
后续代码的扩展和维护就会逐渐变得非常困难且容易出错,
使用卫语句也同样避免不了以上两个问题。
因此根据我的经验,得出一个我个人认为比较好的实践:
if-else 不超过 2 层,块中代码 1~5 行,直接写到块中,否则封装为方法
if-else 超过 2 层,但块中的代码不超过 3 行,尽量使用卫语句
if-else 超过 2 层,且块中代码超过 3 行,尽量使用策略模式
例子:商场打折
代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 商场打折 { class Program { static void Main(string[] args) { Context c = new Context(); c.Set算法(new 打8折()); Console.WriteLine( c.CalResult(100,2)); Console.ReadKey(); } } public class Context { private 算法 sf; public void Set算法(算法 s) { this.sf = s; } public double CalResult(double numa,double numb) { this.sf.单价 = numa; this.sf.数量 = numb; return this.sf.CalResult(); } } public abstract class 算法 { public double 单价 { get; set; } public double 数量 { get; set; } public abstract double CalResult(); } public class 正常收费 : 算法 { public override double CalResult() { return 单价 * 数量; } } public class 满300返100 : 算法 { public override double CalResult() { var s = 单价 * 数量; return Math.Floor(s / 300) * 100 + s; } } public class 打8折 : 算法 { public override double CalResult() { return (单价 * 数量) * 0.8; } } }
例子:临时工工资计算
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 电工临时工工资 { /* 高级工:时薪为25块/小时 中级工:时薪为20块/小时 初级工:时薪为15块/小时 按每天10小时的工作时长来算。 */ class Program { static void Main(string[] args) { Context c = new Context(new Level1()); Console.WriteLine( c.CalGZ()); Console.ReadKey(); Console.ReadKey(); } } public class Context { private CalGZMethod method; public Context(CalGZMethod m) { this.method = m; } public double CalGZ() { return method.CalGz(); } } public abstract class CalGZMethod { public abstract double CalGz(); } public class Level3 : CalGZMethod { public override double CalGz() { return 25 * 10*28; } } public class Level2 : CalGZMethod { public override double CalGz() { return 20 * 10*28; } } public class Level1 : CalGZMethod { public override double CalGz() { return 15 * 10 * 28; } } }
例子:使用字典+Lambda表达式优化简单逻辑的if else
杀鸡焉用宰牛刀?就是几个if else场景我需要用到策略模式?
因此简单的if else可以使用字典+Lambda表达式方式实现函数映射,来实现。
还是上面的例子改造一下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 电工临时工工资 { /* 高级工:时薪为25块/小时 中级工:时薪为20块/小时 初级工:时薪为15块/小时 按每天10小时的工作时长来算。 */ class Program { public enum WorkerEnum { 高级工, 中级工, 初级工 } static void Main(string[] args) { CalGZMethod fun; var p1 = new Level1(); Console.WriteLine( p1.CalGz()); Console.ReadKey(); Dictionary<WorkerEnum, Func<double>> map = new Dictionary<WorkerEnum, Func< double>>(); map.Add(WorkerEnum.高级工, () => { return 25 * 10 * 28; }); map.Add(WorkerEnum.中级工, () => { return 20 * 10 * 28; }); map.Add(WorkerEnum.初级工, () => { return 15 * 10 * 28; }); var result = map[WorkerEnum.高级工]; if(result!=null) { Console.WriteLine(result.Invoke()); } Console.ReadKey(); } } public class Context { private CalGZMethod method; public Context(CalGZMethod m) { this.method = m; } public double CalGZ() { return method.CalGz(); } } public abstract class CalGZMethod { public abstract double CalGz(); } public class Level3 : CalGZMethod { public override double CalGz() { return 25 * 10*28; } } public class Level2 : CalGZMethod { public override double CalGz() { return 20 * 10*28; } } public class Level1 : CalGZMethod { public override double CalGz() { return 15 * 10 * 28; } } }
如果有人说,代码都写在Lambda表达式中,那太长了怎么办?
这个时候我们可以把继承CalGZMethod的三个类用起来。
这个时候实际上是用字典把Context类的功能给替代了。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 电工临时工工资 { /* 高级工:时薪为25块/小时 中级工:时薪为20块/小时 初级工:时薪为15块/小时 按每天10小时的工作时长来算。 */ class Program { public enum WorkerEnum { 高级工, 中级工, 初级工 } static void Main(string[] args) { CalGZMethod fun; var p1 = new Level1(); Console.WriteLine( p1.CalGz()); Console.ReadKey(); Dictionary<WorkerEnum, Func<double>> map = new Dictionary<WorkerEnum, Func< double>>(); //map.Add(WorkerEnum.高级工, () => { return 25 * 10 * 28; }); //map.Add(WorkerEnum.中级工, () => { return 20 * 10 * 28; }); //map.Add(WorkerEnum.初级工, () => { return 15 * 10 * 28; }); map.Add(WorkerEnum.高级工, (new Level3()).CalGz); map.Add(WorkerEnum.中级工, (new Level2()).CalGz); map.Add(WorkerEnum.初级工, (new Level1()).CalGz); var result = map[WorkerEnum.高级工]; if(result!=null) { Console.WriteLine(result.Invoke()); } Console.ReadKey(); } } public interface MyFunction { void apply(); } public abstract class CalGZMethod { public abstract double CalGz(); } public class Level3 : CalGZMethod { public override double CalGz() { return 25 * 10*28; } } public class Level2 : CalGZMethod { public override double CalGz() { return 20 * 10*28; } } public class Level1 : CalGZMethod { public override double CalGz() { return 15 * 10 * 28; } } }
策略模式优点讨论:
策略模式是一种定义了一系列算法的模式,从概念上来看,
所有这些算法完成的都是相同的工作,只是实现不同。
它可以以相同的方式调用所有算法,减少了各种算法类与使用算法类之间的耦合[DPE]。
策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。
继承有助于提取出这些算法中的公共功能[DP]。
另一个策略模式的优点是简化了单元测试,因为每个算法都有自己的类,
可以通过自己的接口单独测试[DPE]
状态模式和策略模式对比:
状态模式和策略模式是很相近的,包括他们的代码和要解决的问题都相差不大,
它们都封装了一系列的算法或者行为,他们的类图看起来几乎如出一辙,
可是从意图上看它们有很大不一样。
相同点:都有一个上下文、一些策略类或者状态类,上下文把请求委托给这些类来执行
区别:策略模式中的各个策略类之间是平等又平行的,它们之间没有任何关系,
因此客户必须熟知这些策略类的做用,以便客户本身能够随时主动切换算法。
可是在状态模式中,状态和状态对应的行为早已被封装好,状态之间的切换也早就被规定,
“改变行为”这件事发生在状态模式的内部,对于客户来讲,不需要了解其中的细节。
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!

