设计模式--策略模式

策略模式:

策略模式定义了算法家族,分别封装起来,让他们可以相互替换,此模式让算法变化,但不会影响到使用该算法的用户.

结构图:

image 

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,其实都是回家,只不过是回家的方式不一样.而且,这三种方式回家有什么关系呢?没有关系,没有关系的话,就可以将他们分开至每一个类.

image

代码:

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类中,可以在使用这些行为的类中消除条件语句.

策略模式就是用来封装算法,我们可以用它来封装任何类型的规则,只要在分析过程中听到需要在不同时间应用不用的业务规则,就可以考虑使用策略模式来 处理

 

任何修改都是需要成本的.

高手和菜鸟的区别就在于高手可以花同样的代价获得最大的利益.或者说,对于同样的需求改变,改动越少越好.

posted @ 2009-04-30 10:18  Localhost  阅读(253)  评论(0编辑  收藏  举报