组合设计模式

260 阅读3分钟

最近学习了OOD设计模式. 有一个模拟鸭子游戏的案例,出自<Head First 设计模式>,讲解了使用面向接口编程以及使用组合模式.


###场景重现 有如下图这样一个类设计图. 有一个名为鸭子的基类,然后可以创建很多类型的鸭子,如: 绿头鸭,红头鸭... 基类中,有quack()swim()以及一个display()三个方法. 绿头鸭和红头鸭继承了鸭子类,那么自然拥有了quack()swim()两个方法,但是还需要去实现一个display()的方法 很容易知道display()用来显示该鸭子的属性.

UML1


###问题 到这里,看起来一切都很好.但是需求增加了,需要添加一个飞的操作,如下图:

UML2
增加一个飞的操作也没问题,在父类中实现一下,很轻松. 可是不然,这时候有了一个问题.我们在创建鸭子的时候,创建了一个橡皮鸭,如下图:
UML3
可以看到,橡皮鸭把quack()方法重写了,因为橡皮鸭不能会飞,所以同样需要把fly()方法也重写一遍. 假设,如果我们还可以创建,烤鸭等等一系列不会飞的鸭子,那么我们都需要进行重写fly()方法. 如果我们在父类中又添加一个方法,可能同样会导致很多子类进行重写. 那么,这还是继承带给我们的好处吗? 将来加入一只木头的诱饵鸭,既不会飞,也不会叫,那么我们需要把quack()fly()这两个方法也全部覆盖掉.


###解法 #####换个思路: 用接口怎样? 如下图: 我们添加两个接口,Flyable提供飞的方法,Quackable提供叫的方法. 让子类去实现各自需要的接口.

UML4
嗯,上面的办法看起来确实不错. 有些子类中的fly()方法和quack()方法的代码实现是一样. 这样一来代码就显得冗余了.


###面向对象的准则

  • 发现变化并且封装变化
    • 找出可变之处,把它独立出来,不要和那些不需要变化的代码混在一起.
    • 一个抽象的过程.
  • 针对接口编程而不是针对现实编程.
  • 优先使用组合而不是继承.

###发现变化并且封装变化

UML5
UML6

如上图,我们可以创建中间类来继承接口,并且实现接口. 既然有不同种类的叫声,那么我们就创建三个中间类,分别去实现不同的叫声. ###针对接口编程

UML7
我们将中间类作为成员变量放到父类当中去. 直接在performQuack()方法中调用中间类的quack()方法. 将子类需要自己实现的方法作为抽象方法.


我们再看一下作为子类需要怎么写.

UML8
可以从上图清楚的看到,子类当中,重写构造方法,然后在构造方法当中,对flyBehavior以及quackBehavior进行实例化.根据不同的需求,来实例化不同的中间类即可. 如此一来,我们把相同的代码放到了中间类去.根据子类自身的不同需求去调用不同的中间类里面的方法.


###总结 这个例子很清晰易懂,弄明白了组合模式的解耦,以及代码的封装和单一原则等. 设计模式还是要好好学的,可能项目中不会太多的用到,但是对于写框架有很大的帮助.

本文大部分内容来源于 码农翻身 刘欣老师的讲课内容.可以微信搜索公众号"码农翻身"关注.