《深入浅出设计模式》学习笔记第一章
原始需求和设计
事情是这样开始的,公司需要做一套程序,鸭子,设计如下:
一个鸭子父类,多个派生类,三个可override的方法。
第一次需求变更
我们要会飞的鸭子!!!!!
所以我们做了如下的更改:
父类加了fly方法,嗯,所有的鸭子都会飞了,需求实现!
问题发生了,因为不是所有的鸭子都会飞
我们可以在派生类中把父类的fly方法中的内容覆盖掉,那么这个鸭子就不会飞了!
那么问题又来了,如果再出现几个新型鸭子都不会飞,是不是每个都得覆盖一遍fly方法啊????
也许,可以用接口?
把每个方法都做成接口,如图:
这是超笨的方法,如果一些鸭子的飞行方式发生变化,那么得改多少个类啊。。。
现在的情况是:
继承不行,因为鸭子的行为(需求)在子类里面不断变化,而使用接口又无法进行复用。
幸好,面向对象软件开发有这样一个原则:
找出应用中可能需要变化的地方,把它们独立起来,不要和那些不需要发生变化的代码混在一起。
这句话另一种思考方式就是:把变化的部分取出并封装起来,以便以后可以轻松的改动或扩展,而不影响其他部分。
所以我们应该把鸭子的行为都提取出来。
根据需求,我们知道鸭子的fly和quack行为经常发生变化,所以我们现在的设计是这样的:
设计原则:
针对接口编程而不是针对实现编程。
这是变化的部分,对于Fly和Quack分别定义接口。
namespace DesignPatterns.Intro.Bases { public interface IFlyBehavior { void Fly(); } } namespace DesignPatterns.Intro.Bases { public interface IQuackBehavior { void Quack(); } }
然后实现几种类型的Fly和Quack:
namespace DesignPatterns.Intro.Derives { public class Squeak: IQuackBehavior { public void Quack() { Console.WriteLine("吱吱"); } } } namespace DesignPatterns.Intro.Derives { public class NormalQuack: IQuackBehavior { public void Quack() { Console.WriteLine("呱呱"); } } } namespace DesignPatterns.Intro.Derives { public class MuteQuack: IQuackBehavior { public void Quack() { Console.WriteLine("---------"); } } }
整合鸭子的行为
让我们来定义鸭子:
namespace ConsoleApp2.Bases { public abstract class Duck { private readonly IFlyBehavior _flyBehavior; private readonly IQuackBehavior _quackBehavior; protected Duck(IFlyBehavior flyBehavior = null, IQuackBehavior quackBehavior = null) { _flyBehavior = flyBehavior ?? new FlyNoWay(); _quackBehavior = quackBehavior ?? new MuteQuack(); } public abstract void Display(); public void PerformFly() { _flyBehavior.Fly(); } public void PerformQuack() { _quackBehavior.Quack(); } public void Swim() { Console.WriteLine("所有的鸭子都会游泳"); } } }
这是鸭子的抽象类。
建立实际的鸭子:
namespace ConsoleApp2.Derives { public class MallardDuck: Duck { public MallardDuck(IFlyBehavior flyBehavior = null, IQuackBehavior quackBehavior = null) : base(flyBehavior, quackBehavior) { } public override void Display() { Console.WriteLine("我是个野鸭..."); } } }
测试鸭子
namespace ConsoleApp2 { class Program { static void Main(string[] args) { var duck = new MallardDuck(new FlyNoWay(), new NormalQuack()); duck.PerformFly(); duck.PerformQuack(); duck.Display(); Console.ReadLine(); } } }
这时,需求终于完成了!
我们的鸭子根据传入的Fly和Quack实现类不同而具有不同的效果!
需求又变了,要求鸭子的行为可以随时改变
这时,我们需要动态设定行为,我们只需要加入Set方法即可:
Duck最新的代码是:
namespace ConsoleApp2.Bases { public abstract class Duck { public IFlyBehavior FlyBehavior { private get; set; } public IQuackBehavior QuackBehavior { private get; set; } protected Duck(IFlyBehavior flyBehavior = null, IQuackBehavior quackBehavior = null) { FlyBehavior = flyBehavior ?? new FlyNoWay(); QuackBehavior = quackBehavior ?? new MuteQuack(); } public abstract void Display(); public void PerformFly() { FlyBehavior.Fly(); } public void PerformQuack() { QuackBehavior.Quack(); } public void Swim() { Console.WriteLine("所有的鸭子都会游泳"); } } }
测试效果:
namespace ConsoleApp2 { class Program { static void Main(string[] args) { var duck = new MallardDuck(); duck.PerformFly(); duck.PerformQuack(); duck.Display(); duck.FlyBehavior = new FlyWithWings(); duck.QuackBehavior = new Squeak(); duck.PerformFly(); duck.PerformQuack(); Console.ReadLine(); } } }
需求完成!!!
最终结构如下: