设计模式--策略模式
策略模式:
策略模式定义了算法家族,分别封装起来,让他们可以相互替换,此模式让算法变化,但不会影响到使用该算法的用户.
结构图:
strategy就是个策略类,里面记录了客户需要的方法,此时,客户不一定指客户端,而是指调用这个类的类,我们也可以称之为客户.策略类是个抽象类(抽象策略类),并不实现任何方法,由其具体子类(具体策略类)来实现.
Context只需要保持一个对策略类的引用,然后在context类中调用即可,返回抽象策略类中定义的方法.
适用场景:
1.在运行时需要动态的设置行为时
2.对客户隐藏类的详细实现
优点:
1.行为的实现是通过组合,而不是继承,更有利于代码的复用
2.策略测试更加容易,只需单独的测试每个策略即可.在增加新的策略的情况下,也只需要对新增的策略进行测试,而不用对所有的策略进行测试
3.有效的提供了代码的封装,同时,当策略增加或者减少时,都不会影响到Context类的变化避免了对if else或者switch语句的判断操作.
缺点:
1.当增加策略时,会增加相应的类,维护系统的任务要加重
2.策略模式需要客户端知道所有的策略(行为)
例子:
人下班总是要回家的,有人走路回家,有人骑自行车回家,有人开车回家,该如何写呢?
public class Person { public void GoHomeOnFoot() { //TODO } public void GoHomeByBike() { //TODO } public void GoHomeWithCar() { //TODO } }也可以这么定义这个类:
public class Person { public string GoHome(string type) { switch (type) { case "foot": return "onfoot"; case "car": return "withcar"; case "bike": return "bybike"; default: return string.Empty; } } }那么页面调用时,只需要写:
Person p = new Person(); p.GoHomeWithCar(); or Person p = new Person(); p.GoHome("car");即可.但是,当增加新的回家方式(GoHomeByBus)时,该怎么办呢?修改Person类,同时修改客户端.
这就违背了 开放封闭原则.
采用策略模式,该如何处理呢?
不论是GoHomeOnFoot,还是GoHomeByBike,还是GoHomeWithCar,其实都是回家,只不过是回家的方式不一样.而且,这三种方式回家有什么关系呢?没有关系,没有关系的话,就可以将他们分开至每一个类.
代码:
public abstract class GoHome { public abstract string Go(); } public class GoHomeWithCar : GoHome { public override string Go() { return "GoHomeWithCar"; } } public class GoHomeByBike : GoHome { public override string Go() { return "GoHomeByBike"; } } public class GoHomeOnFoot : GoHome { public override string Go() { return "GoHomeOnFoot"; } } public class Person { GoHome context; public Person() { } public Person(GoHome context) { this.context = context; } public void SetGoHomeBehavior(GoHome context) { this.context = context; } public string GoHome() { return context.Go(); } }页面调用:
protected void Page_Load(object sender, EventArgs e) { GoHome g = new GoHomeByBike(); Person p = new Person(g); Display(p); p.SetGoHomeBehavior(new GoHomeWithCar()); Display(p); } private void Display(Person p) { Response.Write(p.GoHome()+"<br/>"); }结果:
GoHomeByBike GoHomeWithCar这样设计的话,当新增一个GoHomeByBus方法时,只需要建立一个类,继承GoHome类即可.实现Go方法.
页面调用处,在调用到该方法时,进行修改即可.此时避免了修改原文中的Person类,保证了其他几种回家方式的正确性.确保回家方式被扩展时,它们没有受到影响.
对于回家方式.可以再页面设置一个DropDownList,对回家方式进行绑定.同时,采用反射机制,基本可以保证客户端也不需要什么改变,只需要增加回家方式即可.
涉及到的设计原则
1.多用组合,少用继承
组合是一种“HAS-A”关系,而继承是一种“IS-A”关系。很明显“HAS-A”要比“IS-A”更灵活一些。
也就是说在创建系统的时候,我们应该优先使用对象组合,因为它不仅可以给你提供更多灵活性和扩展性,而且还使你可以在运行时改变行为(组合不同的对象)!但是也不是说继承就是不能用,只是说应该把继承应用在相对更稳定,几乎没有变化的地方.
2.找到系统中变化的部分,将变化的部分同稳定的部分分开.
稳定的部分可以通过继承的方式来实现.而变化的部分,则使用策略模式.
3.面向接口编程,而不是面向实际编程
此处的面向接口编程,并不单单指接口,而是指超类,接口.
面向接口编程时,我们可以使用多态,通过一些其他的简单模式,可以再客户端不需要改变的时候,对系统进行扩展.
面向实际编程,就想 实际 连接 实际
面向接口编程,就像 实际 连接 接口(超类) 连接 实际
接口在其中搭起了一座桥梁.在其中,可以根据客户的需要,具体的调用哪个实际类
面相接口编程中,并不代表不会使用new,new是不可避免要使用的.除非总是使用静态方法.面向接口编程时,是指实际客户不用new一个实际,而会通过其他方式获得该实际的引用,或方法的引用.从而实现解耦合.
下面一段话摘自<<大话设计模式>>一书,做了部分修改:
在软件设计时,有时不得不在客户端的代码中为了判断用哪个算法而使用了switch,或者if..else..,这是正常的,因为当不同的行为堆砌在一个类中的时候,就很难避免使用条件语句来选择合适的行为.将这些行为封装在一个个独立的strategy类中,可以在使用这些行为的类中消除条件语句.
策略模式就是用来封装算法,我们可以用它来封装任何类型的规则,只要在分析过程中听到需要在不同时间应用不用的业务规则,就可以考虑使用策略模式来 处理
任何修改都是需要成本的.
高手和菜鸟的区别就在于高手可以花同样的代价获得最大的利益.或者说,对于同样的需求改变,改动越少越好.