一开始设计的 超类 Duck ,具有共同具体行为 呱呱叫(quack) 、游泳(swim)、 和 抽象行为 外观(display)。
每个鸭子子类 实现自己的 display() 比如 MallardDuck(野鸭子) 外观是绿头 。 RedHeadDuck (红头鸭)外观是红头,以及更多类型的鸭子 去继承Duck类 然后实现display()。
当有一天,需求来了,现在要让Duck(鸭子)可以飞, 那我们要怎样修改程序呢?
让我设计的话,首先想到的是在Duck 这个超类上面 添加一个fly() 行为,这样继承Duck 类的 具体子类都会有飞。
但是如果真的在Duck 上加上一个fly 那么会不会引出新的问题呢?
我同joe 一样忽略了一个问题, 在超类中添加 fly(),就会导致所有的子类都具备fly(),连那些不具备fly() 的子类也无法避免,并不是所有的子类都需要这个行为(并不是所有的鸭子都会飞)。
出现这样的问题,该怎么解决呢?通过继承,然后覆盖子类方法 不就可以实现避免类似 橡皮鸭 fly() 这样的事情了吗?
通过上图,可以得出结论,在超类中声明行为,在子类中继承超类中提供的行为有以下缺点: 答案是 ABDF
通过继承,每次我们有新的子类 去继承Duck,那么就可能会重写 叫 飞 等等行为。这是很大的工作量,因为我们需要想其他办法去解决这个问题。
这样可以解决当前面临的问题,但是如果子类有很多个,很多子类都要写一遍fly(),这部分代码是重复的,某一天要修改一下子类的fly()行为, 那按照当前的方式,又要对每一个子类的fly 行为去修改一遍。
我们需要一种尽可能改动小的方法,因为再开发中唯一不变的就是需求的不断变化。
因为,要解决问题,我们需要把变化的东西与不变的东西分离出来,不要因为频繁变化的东西 导致我们做不必要的改动。
那么 哪些是变化的? 哪些是不变的呢?
鸭子的 游泳(swim) 和 外观(display) 应该是不变的,所有的鸭子应该都会游泳,都有外观 两个具体行为。
鸭子的叫声 和 是否会飞 是变化的。
因此我们将不变的封装在鸭子 这个超类中,将变化的行为抽象出来,以后变化的时候,只改变化的这一部分就好了。
接下来我们就针对变化的行为去设计,针对接口编程。
将飞行行为抽取出来, 然后针对具体类实现具体的飞行行为, 分为 可以飞行 和不可以飞行 两种。
将叫声行为抽取出来, 然后对具体的类实现具体的叫声行为,分为 咕咕叫 哗哗叫 呱呱叫 等。
抽取方式有两种,抽象类 和接口,那么我们改怎么选择呢?
我们不需要知道实际的子类类型是什么,我们只关心如何正确的执行,我们声明的动作就够了。