标签:pos 委托 super tin 识别 添加 aries sig 组成
最近看了《Head First Design Patterns》这本书。正如其名,这本书讲的是设计模式(Design Patterns),而这本书的第一章,讲的是很重要的一些设计原则(Design Principles)。
Identify the aspects of your application that vary and separate them from what stays the same.(识别应用程序中各个方面的变化,并将它们与保持不变的部分分开。)
Program to an interface, not an implementation.(面向接口而不是实现编程。)
Favor composition over inheritance.(优先考虑组成而不是继承。)
其中令我感触颇深的是,“面向接口而不是实现编程”颠覆了我一直以来的认识。
文章中示例代码为原书中截图,C#代码参照文末提供链接。
书中用了一个很形象的示例:模拟鸭子程序(SimUDuck)。系统的最初设计使用标准的OO技术,并创建了一个Duck基类,所有其他Duck类型都继承自该基类。

设计系统时考虑到鸭子都会发出叫声,而且都会游泳,于是将Quack方法和Swim方法定义到Duck基类中并实现;此外,并不是所有的鸭子都是长得一样的,那么将Display方法在Duck基类中定义为抽象的,所有继承自Duck基类的子类编写自己的实现。
新的需求产生了!我们需要让系统中的鸭子可以飞。从面向对象的角度来考虑,如果我们想要代码重用,只需要在Duck基类中添加方法Fly并实现它——所有的鸭子子类都是继承自Duck基类的——就实现了让鸭子飞的功能。我们通过继承实现了代码重用,很轻松就解决了问题。
也许我们需要深入考虑一下,所有的鸭子都会飞吗?玩具橡胶鸭呢?我们把Fly方法的定义及实现放到了Duck基类中,所有继承自它的子类都继承到了Fly方法,其中也包括了不应继承Fly方法的子类。如果按照上面的方案,那我们只能在RubberDuck橡胶鸭子类中重写父类的Fly方法让RubberDuck执行Fly的时候什么都不做。
再深入一些,如果我们的系统中除了橡胶鸭外,还有其他各种鸭子,比如木头鸭子呢?这时DecoyDuck木头鸭子继承来的Quack方法出现了问题——木头鸭子不会叫!我们只好再把DecoyDuck中的Quack方法重写了......
如果我们改用接口会怎么样呢?把Quack和Fly方法从基类中拿出来,分别在IQuackable和IFlyable接口中定义,然后我们不同的子类根据需要来继承接口,并实现Quack或Fly方法。

当我们有很多个子类鸭子的时候,就要分别为每个继承了IQuackable或IFlyable接口的子类来编写Quack或Fly的实现方法,这完全破坏了代码重用!值得注意的是,虽然我们在这里使用了接口,但这并不是面向接口编程。
这里引入第一条设计原则:Identify the aspects of your application that vary and separate them from what stays the same.(识别应用程序中各个方面的变化,并将它们与保持不变的部分分开。)
换言之:take the parts that vary and encapsulate them, so that later you can alter or extend the parts that vary without affecting those that don’t.(将变化的部分封装起来,以便以后可以更改或扩展变化的部分而不会影响那些不变的部分。)
这样带来的好处是,我们可以进行更少的代码更改来实现需求功能,减少因代码更改而带来的意想不到的影响,并且提高了系统灵活性。
我们知道Duck的不同子类中,Quack和Fly的行为是会发生变化的,那么我们将Quack和Fly方法从Duck基类中拿出来,并为Quack和Fly方法分别创建一些类,来实现各种不同的行为。

设计原则:Program to an interface, not an implementation.(面向接口而不是实现编程。)
在这里,面向接口而不是实现编程,和封装变化是相辅相成的。值得注意的是,这里所说的接口,并不是我们代码层面上的interface,"面向接口编程(Program to an interface)所表达的意思实际上是面向基类编程(Program to a supertype),核心思想是利用面向对象编程的多态性。在代码的具体实现上,我们既可以用Interface来作为我们所面向的接口,也可以用一个抽象的基类来作为我们面向的接口。遵循面向接口编程,对模拟鸭子程序的Fly和Quack行为进行设计,我们可以定义接口IFlyBehavior、IQuackBehavior来代表行为Fly和Quack,接口的实现则是行为具体的表现形式。我们可以将接口的不同实现类,来赋值给Duck的不同子类,从而利用继承及多态来实现面向接口编程。类图如下:

FlyBehavior是一个所有不同的Fly类都要继承的接口或基类,其中定义了Fly方法。不同的Fly类有不同的Fly方法实现。QuackBehavior类似。
接下来我们对Duck类进行更改,将Fly和Quack委托出去,不再通过Duck类或其子类的方法来实现。
首先我们在Duck类中定义两个代表FlyBehavior和QuackBehavior的变量。这两个变量的值是不同的Duck所需要的特定FlyBehavior、QuackBehavior的子类:
然后实现PerformQuack方法:
为FlyBehavior和QuackBehavior赋值:
至此我们就实现了面向接口编程。
我们还可以动态设置Duck的行为,只需要为Duck类的FlyBehavior、QuackBehavior提供Set方法(在C#中,使用自动属性即可)。
Favor composition over inheritance.(优先考虑组成而不是继承。)
HAS-A(有一个)比IS-A(是一个)要好。HAS-A在我们的Duck系统中可以描述为:每一个Duck都HAS-A有一个FlyBehavior,还HAS-A有一个QuackBehavior,Duck委托它们来处理Fly和Quack的行为。优先考虑组合而不是继承让我们的系统拥有更多的灵活性,封装变化,还可以在运行时动态更改类的行为。
标签:pos 委托 super tin 识别 添加 aries sig 组成
原文地址:https://www.cnblogs.com/realZhangChi/p/12186474.html