勇哥注:
策略模式和状态模式是两个亲兄弟,类图很像。
策略模式是封装了一系列算法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同。就像是同样是亿万富豪,马云是靠卖东西,王思聪是靠继承。
状态模式的每种状态做的事,可以完全不同。

按类图编写的代码:
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
版权声明:本文为博主原创文章,转载请附上博文链接!


少有人走的路


















