策略模式

缘起

每周一篇的计划刚定下就迎来了第一周,那我该写些什么呢?思考了一下,刚好最近在看Head First 设计模式一书,那么我就来说说设计模式吧。虽然关于设计模式的博客,博客园里已经有很多大佬写过了,我就权当加深自己的印象和理解。

开始

首先,我们来提一个需求。我们拥有一款鸭子模拟游戏,现在我们有两种鸭子类型,一种绿头鸭(MallardDuck),一种红头鸭(RedheadDuck),它们都拥有外貌描述(dispaly)、叫(quack)和飞(fly)的方法。
那么代码会是这样的

public abstract class Duck
{
    public abstract void Display();
    public void Quack()
    {
        Console.WriteLine("呱呱");
    }
      public void Fly()
    {
        Console.WriteLine("飞行");
    }
}
public class MallardDuck:Duck
{
    public override void Display()
    {
        Console.WriteLine("我是绿头的");
    }
}
public class RedheadDuck:Duck
{
    public override void Display()
    {
        Console.WriteLine("我是红头的");
    }
}

这样已经满足了我们的需求,但这时产品又提出了一个需求,我想要增加一种新的鸭子类型-橡皮鸭(RubberDuck)。这简单,我们这样做:

public class RubberDuck:Duck
{
    public override void Display()
    {
        Console.WriteLine("我是橡皮鸭的");
    }
}

是不是看出来哪里不对了。是的,橡皮鸭怎么会飞呢,而且它也不是“呱呱”的叫。当需求改变时,为了复用之前的代码,我们用了继承,结局并不完美。

当涉及“维护”时,为了“复用”(reuse)目的而使用继承,结局并不完美。

如何解决呢?也很简单。我们重写Duck的Fly方法和Quack方法,并且Fly方法什么也不干。

public class RubberDuck:Duck
{
    public override void Display()
    {
        Console.WriteLine("我是橡皮鸭的");
    }
    public override void Quack()
    {
        Console.WriteLine("嘎嘎");
    }
    public override void Fly()
    {

    }
}

可是这样就完美了吗?每当有新的鸭子类出现,我们就要被迫检查并可能需要覆盖Fly()和Quack()方法。那我们将Fly()从超类中提取出来,放进一个IFlyable接口,只有会飞的鸭子才实现此接口,这种解决方法让我们必须去实现接口的方法,代码无法复用,只是从一个噩梦跳入另一个噩梦。那么这个问题是不是无解了?

刚好我们最近看了Head First设计模式,看看有没有适合我们使用的方式。

找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

Duck类仍是超类,但我们将会变化的部分,也就是Fly和Quack取出。

接着我们设计鸭子的行为

针对接口(超类)编程,而不是针对实现编程。

根据设计原则,我们将这样设计

public interface IFlyBehavior
{
    void Fly();
}
public class FlyNoWay : IFlyBehavior
{
    public void Fly()
    {
        Console.WriteLine("不会飞");
    }
}
public class FlyWithWings : IFlyBehavior
{
    public void Fly()
    {
        Console.WriteLine("飞行");
    }
}

将Fly行为定义为接口,并让FlyNoWay类和FlyWithWings类实现它,Quack行为同理。接着修改Duck类,申明两个接口对象,添加PerformQuack方法和PerformFly方法来实现原先的Quack和Fly方法。

public abstract class Duck
{
    public IFlyBehavior flyBehavior;
    public IQuackBehavior quackBehavior;

    public void PerformQuack()
    {
        quackBehavior.Quack();
    }

    public void PerformFly()
    {
        flyBehavior.Fly();
    }

    public abstract void Display();
}

让我们来调用看看:

class Program
{
    static void Main(string[] args)
    {
        var mallard = new MallardDuck();
        mallard.PerformQuack();
        mallard.PerformFly();
    }
}

结果是正确的,绿头鸭会呱呱叫也会飞。我们接着优化一下,可以动态的设定行为。

public abstract class Duck
{
    public IFlyBehavior flyBehavior;
    public IQuackBehavior quackBehavior;

    public void PerformQuack()
    {
        quackBehavior.Quack();
    }

    public void PerformFly()
    {
        flyBehavior.Fly();
    }

    public abstract void Display();

    public void setFlyBehavior(IFlyBehavior fb)
    {
        flyBehavior = fb;
    }

    public void setQuackBehavior(IQuackBehavior qb)
    {
        quackBehavior = qb;
    }
}

新加setFlyBehavior和setQuackBehavior方法,让我们可以动态的传入鸭子的行为,接着我们来运行一下

class Program
{
    static void Main(string[] args)
    {

        var mallard = new MallardDuck();
        mallard.PerformQuack();
        mallard.PerformFly();

        mallard.setFlyBehavior(new FlyNoWay());
        mallard.setQuackBehavior(new QuackNoWay());

        mallard.PerformQuack();
        mallard.PerformFly();
    }
}

策略模式:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

结语

感觉没有对策略模式总结的很到位,第一次写技术的博客也没什么思路,基本按书本的流程走下来,废话偏多,文章也不够简洁,接下去会多多思考,多多总结,提炼出文章中的精华。

原文地址:https://www.cnblogs.com/jiuxi/p/11973000.html