概述:
策略模式属于对象的行为模式(has-a, not is-a)。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
意图:
- 策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。
- 策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
- 运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但是同时只能使用一个。
参与者:
- 环境(Context)角色:需要使用ConcreteStrategy提供的算法;内部维护一个Strategy的实例;负责动态设置运行时Strategy具体的实现算法;负责跟Strategy之间的交互和数据传递。
- 抽象策略(Strategy)角色:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。
- 具体策略(ConcreteStrategy)角色:实现了Strategy定义的接口,提供具体的算法实现。
UML图:
实质:
策略模式中的Context类的功能基本上就是对工厂类的强化版本,它也负责根据输入参数来生成不同的类,只是它并不返回生成类,而是将生成类所实现的功能接口包装一次,提供给客户。这样对客户来说,他们只需要知道这一个Context类就可以完成他们想要的功能,而不必再知道其他的信息。
public interface IStrategyA { //该方法对于Context来说,该行为不稳定,是动态的,如果在运行时才确定具体行为,所以用has-a 的关系会比is-a的关系好 void AlgorithmA(); } public interface IStrategyB { //该方法对于Context来说,该行为不稳定,是动态的,如果在运行时才确定具体行为,所以用has-a 的关系会比is-a的关系好 void AlgorithmB(); } public class StrategyA:IStrategyA{ public void AlgorithmA() { //具体实现策略A } } public class StrategyAA : IStrategyA { public void AlgorithmA() { //具体实现策略AA } } public class StrategyB : IStrategyB { public void AlgorithmA() { //具体实现策略B } } public class StrategyBB : IStrategyB { public void AlgorithmB() { //具体实现策略BB } }
public interface IAction { //该方法对于Context来说很稳定,该行为确定了,客户端不需要动态改变其行为,所以用is-a的关系会比has-a的关系好 void Action(); }
//AContext具备IAction、IStrategyA、IStrategyB接口行为 public abstract class AContext:IAction { //如果IStrategy行为是不固定的。继承IStrategyA、IStrategyB方式也可以实现该功能,但是需要为每一个特殊的子类提供具体行为的实现。 IStrategyA strategyA; //而且如果象这种IStrategy有多种的话,继承代码重用功力很差了 IStrategyB strategyB; public AContext(IStrategyA strategyA,IStrategyB strategyB) { this.strategyA = strategyA; this.strategyB = strategyB; } public void ContextInterfaceA() { strategyA.AlgorithmA(); } public void ContextInterfaceB() { strategyB.AlgorithmB(); } public void Action() { //具体Action行为 } }
优点:
- 提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。
我的理解:结合上序代码,如果多项行为在子类里持续不断地改变在运行时才确定,所以让所有的子类都拥有基类的行为是不适当的,而使用实现接口的方式,又破坏了代码重用。找到系统中变化的部分,将变化的部分同其它稳定的部分隔开。变化的部分就是子类里的行为,所以我们要把这部分行为封装起来,去动态地改变一个子类的行为,运行时确定具体子类实例的行为。通过继承的话我们则需要为每一个特殊的子类提供具体行为的实现。
- 简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
- 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
- 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
缺点:
- 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。
- 在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象。(这本身没有解除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由Context来承担,这就最大化的减轻了客户端的压力。)
使用场景:
- 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
- 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。(具体实现可任意变化或扩充)
- 对客户(Duck)隐藏具体策略(算法)的实现细节,彼此完全独立。
简单工厂改进策略模式(也可以通过反射改进,这里不再介绍)
View Code
//简单工厂改进策略模式(也可以通过反射改进,这里不再介绍) public abstract class AContextFactory : IAction { IStrategyA strategyA; IStrategyB strategyB; public AContextFactory(string typeA, string typeB) { switch (typeA) { case "StrategyA": this.strategyA = new StrategyA(); break; case "StrategyAA": this.strategyA = new StrategyAA(); break; } switch (typeB) { case "StrategyB": this.strategyB = new StrategyB(); break; case "StrategyBB": this.strategyB = new StrategyBB(); break; } } public void ContextInterfaceA() { strategyA.AlgorithmA(); } public void ContextInterfaceB() { strategyB.AlgorithmB(); } public void Action() { //具体Action行为 } }
总结:以上纯属个人的理解,对于有些地方觉得还是理解不是很深,有不足之处和错误的地方希望大家帮我指出。谢谢