设计模式(二): 策略模式
概要:
策略模式(Strategy):他定义了算法家族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化,不会影响到算法的客户端。
当有一组算法完成的都是相同的工作,只是实现不同,以相同的方式调用所有的算法。在这种情况下可以使用策略模式,可减少各类算法类与使用算法类之间的耦合。
问题初探:
这边就以简单的数字加减举例。
1 static class Calculate 2 { 3 private static string Add = "+"; 4 private static string Subtract = "-"; 5 6 public static double Calac(double a, double b, string symbol) 7 { 8 double result = 0.0; 9 if (symbol == Add) 10 { 11 result = a + b; 12 } else if (symbol == Subtract) 13 { 14 result = a - b; 15 } 16 17 return result; 18 } 19 }
这样写的化,在后期代码扩展*或者/的时候,就需要添加新的if 判断,代码臃肿且扩展性低。
如果使用策略模式,将各类运算归结到不同的策略中,会有很强的扩展性。那下面看下该模式。
模式讲解:
策略模式结构图:
组成:
1.抽象策略角色(Straetegy): 这是一个抽象类,通常情况下使用接口或抽像类去实现。
2.具体策略角色(ConcreteStrategyA....): 包装了具体的算法和行为。也就是实现 抽象策略角色 接口的实现类。
3.环境角色(Context): 持有一个抽象角色的引用,给客户端调用。
结构图的实现:
Strategy 类,定义所有支持的算法和公共接口:
1 abstract class Strategy 2 { 3 // 算法方法 4 public abstract void Algorithmlnterface(); 5 }
ConcreteStrategy,封装了具体的算法或行为,继承于Strategy.
1 // 具体算法A 2 class ConcreteStrategyA : Strategy 3 { 4 public override void Algorithmlnterface() 5 { 6 Console.WriteLine("算法A 的实现"); 7 } 8 } 9 10 // 具体算法B 11 class ConcreteStrategyB : Strategy 12 { 13 public override void Algorithmlnterface() 14 { 15 Console.WriteLine("算法B 的实现"); 16 } 17 } 18 19 // 具体算法 C 20 class ConcreteStrategyC : Strategy 21 { 22 public override void Algorithmlnterface() 23 { 24 Console.WriteLine("算法C 的实现"); 25 } 26 }
Context,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用。
// 上下文 class Context { Strategy strategy; // 初始化时,传入具体的策略对象 public Context(Strategy strategy) { this.strategy = strategy; } // 上下文 public void ContextInterface() { // 根据具体的策略对象,调用其算法的方法 strategy.Algorithmlnterface(); } }
客户端代码
1 static void Main(string[] args) 2 { 3 Context context; 4 5 // 由于实例化不同的策略,所以最终在调用 context.ContextInterface(); 时,所获得的结果就不尽相同 6 context = new Context(new ConcreteStrategyA()); 7 context.ContextInterface(); 8 9 context = new Context(new ConcreteStrategyB()); 10 context.ContextInterface(); 11 12 context = new Context(new ConcreteStrategyC()); 13 context.ContextInterface(); 14 }
问题改进:
了解了策略模式,那改写一下上面的加减算法吧。
1 // 定义抽象类接口 2 public interface ICalculator 3 { 4 double Calc(double a, double b); 5 } 6 7 // 加减的实现 8 public class Add : ICalculator 9 { 10 public double Calc(double a, double b) 11 { 12 return a + b; 13 } 14 } 15 16 public class Sub : ICalculator 17 { 18 public double Calc(double a, double b) 19 { 20 return a - b; 21 } 22 } 23 24 // 上下文 25 public class ClcContext 26 { 27 private ICalculator mcalculator; 28 29 public ClcContext(ICalculator calculator) 30 { 31 this.mcalculator = calculator; 32 } 33 34 public double Clc(double a, double b) 35 { 36 return mcalculator.Calc(a, b); 37 } 38 }
客户端这样调用就可以了
1 static void Main(string[] args) 2 { 3 // 客户端调用 4 ICalculator icalculator = new Add(); 5 ClcContext context = new ClcContext(icalculator); 6 double result = context.Clc(1,2); 7 }
如果后期在加乘法的扩展就会方便很多,添加类继承即可。
特点:
- 抽象类中可以定义一系列可供重复使用的算法或行为,继承有助于取出这些算法中的公共功能。
- 简化了单元测试,每个算法有自己的类,可以通过自己的接口单独测试。
- 当不同的行为堆砌在一个类中,就需要使用条件语句来选择合适的方法,当使用策略模式时,可消除条件判断。
- 策略模式就是用来封装算法,但在实现中,几乎可以用他封装任何类型的规则,只要在分析过程中需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化。
策略模式还可以于 简单过程模式 结合,使代码的耦合度更加降低。