Head First设计模式01 入门

"设计模式入门"

Posted by 小镇青年 on December 26, 2018

本文为HeadFirst读书笔记

需求

设计一个鸭子模拟游戏,其中有各种不同类型的鸭子,有不同行为,如呱呱叫,游泳。

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class Duck
{
    public abstract void Display();
    public void Quack()
    {
        Console.WriteLine("quack");
    }
    public void Swim()
    {
        Console.WriteLine("I'm swiming");
    }
}

需求变更:新增功能,让鸭子飞

  • 思路一:在基类Duck中新增Fly()方法。 在Duck基类中增加Fly()方法,所有鸭子都会继承Fly()方法。但是并不是所有的鸭子都会飞,比如橡皮鸭子。
  • 思路二:把Fly()从基类中抽取出来,放进一个Flyable接口中,这么一来,只有会飞的鸭子才会实现此接口。但是这并不是一个好办法,这回导致重复代码变多。比如有48个Duck的子类都需要修改一下飞行行为,那如何处理?

继承并不能很好的解决此问题,因为鸭子的行为在子类中不断改变,并且让所有的子类都有这些行为是不恰当的。Flyable接口一开始似乎是很不错,解决了问题(只有会飞的鸭子才能继承Flyable)。但是C#中接口不具代码实现,所以实现接口无法达到代码的服用。这就是说,假如你修改了某个行为,比如飞行行为,你必须在每一个类中跟踪修改飞行行为。

设计原则1

找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。换言之,每次新需求一来,都会使某些方面的代码发生变化,那么此时就需要把这部分代码抽出来,和其他稳定部分有所区别。

分开变化和不会变化的部分

变化部分:Fly,Quack。为了分开变化部分和不会变化部分,鸭子的FlyQuack会随鸭子的不同而改变。因此我们准备建立两组类,一个实现飞行,一个实现叫声。

设计原则2

针对接口编程,而不是针对实现编程。

我们利用接口代表每个行为,比如FlyBehavior表示飞行行为,QuackBehavior表示鸭子叫的行为。所以鸭子类不会实现这两个接口,而有其他行为类实现这俩接口。以前的做法是:行为来自Duck的基类的具体实现,或者基类继承某个接口然后又子类实现。这两种做法都依赖于实现。如下: 飞行行为接口及实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public interface FlyBehavior
{
    void Fly();
}
public class FlyWithWings : FlyBehavior
{
    public void Fly()
    {
        Console.WriteLine("I'm flying");
    }
}
public class FlyNoWay : FlyBehavior
{
    public void Fly()
    {
        Console.WriteLine("I can't fly");
    }
}
public class FlyRocketPowered : FlyBehavior
{
    public void Fly()
    {
        Console.WriteLine("I'm flying with a rocket!");
    }
}

叫声行为接口及实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public interface QuackBehavior
{
    void Quack();
}
public class CommonQuack : QuackBehavior
{
    public void Quack()
    {
        Console.WriteLine("quack");
    }
}
public class MuteQuack : QuackBehavior
{
    public void Quack()
    {
        Console.WriteLine("silence");
    }
}
public class Squeak : QuackBehavior
{
    public void Quack()
    {
        Console.WriteLine("squeak");
    }
}

鸭子基类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public abstract class Duck
{
    protected FlyBehavior flyBehavior;
    protected QuackBehavior quackBehavior;

    public Duck()
    {
    }
    public abstract void Display();

    public void performFly()
    {
        flyBehavior.Fly();
    }
    public void performQuack()
    {
        quackBehavior.Quack();
    }
    public void Swim()
    {
        Console.WriteLine("swiming");
    }
    public void SetFlyBehavior(FlyBehavior fb)
    {
        flyBehavior = fb;
    }
    public void SetQuackBehavior(QuackBehavior qb)
    {
        quackBehavior = qb;
    }
}

鸭子子类:绿头鸭

1
2
3
4
5
6
7
8
9
10
11
12
public class MallardDuck : Duck
{
    public MallardDuck()
    {
        flyBehavior = new FlyWithWings();
        quackBehavior = new CommonQuack();
    }
    public override void Display()
    {
        Console.WriteLine("I'm a real Mallard duck");
    }
}

鸭子子类:模型鸭

1
2
3
4
5
6
7
8
9
10
11
12
public class ModelDuck : Duck
{
    public ModelDuck()
    {
        flyBehavior = new FlyNoWay();
        quackBehavior = new CommonQuack();
    }
    public override void Display()
    {
        Console.WriteLine("I'm a model duck");
    }
}

本文Github源码