
设计模式之-降龙十八掌
温馨提示:本文代码较多且综合了好几种设计模式使用,建议收藏或者慢慢观看。代码区域可以左右滑动
本文将综合抽象工厂、组合模式、装饰器模式、适配器模式、观察者模式等模式运用在本例子中。对于不熟悉这些模式的读者可以阅读历史文章学习加强自己的代码心法。完整代码在 github:https://github.com/UniqueDong/zero-design-patterns 对应的 com.zero.headfirst.verb 包目录下。
适配器模式
首先我们从制造一个屏幕模拟器开始,模拟鸭子叫,根据需求我们先定义 Quackable接口,然后分别定义MallarDuck、RedheadDuck、RubberDuck、DuckCall等不同的鸭子实现呱呱叫接口,这个时候有鸭子出现的地方也出现了鹅混在其中,我们就通过适配器模式适配成鸭子。
现在各种鸭子定义好了,我们定义鹅 Goose
这个时候需要适配器将鹅适配成鸭子,适配器持有鹅的引用,鹅就是被适配对象,同时适配器实现 Quackable接口,当调用 quack()的时候实际是委托调用了honk()方法。
最后我们的屏幕模拟器登场,展示满屏呱呱叫的鸭子与鹅。
装饰器-统计鸭子叫的次数
现在继续新增需求,在鸭子类不修改的情况下我们要统计叫的次数。 我们创建一个装饰者,通过把鸭子包装进装饰者对象,然后给鸭子新的功能(计算叫的次数)。装饰器也要实现 Quackable接口,并且持有鸭子实例变量-被装饰者,内部使用一个静态变量保存叫的次数,当 quack()被调用的时候我们就把调用委托给被装饰的 Quackable对象,并且把叫的次数加 1。
利用装饰器模式-我们实现了不修改鸭子类却又给鸭子新增了功能。接着我们需要修改屏幕模拟器,将需要统计叫声的鸭子包装在装饰器中。
抽象工厂
写到这里我们已经用上了适配器模式、装饰器模式。有没有觉得我们创建鸭子都是 new 出来的?为什么不把创建鸭子的的程序集合集中在一个地方,换句话说就是将创建于修饰的部分包装起来。就是我们接下来要说的:工厂模式。
我们定义一个工厂,生产各种不同类型的鸭子产品家族,所以我们使用抽象工厂模式。
首先从定义抽象工厂 AbstractDuckFactory开始,用于创建不同类型的鸭子家族。
接着创建一个普通鸭子工厂继承抽象工厂,该工厂创建没有装饰器装饰的鸭子。屏幕模拟器并不知道实际的产品是什么,只知道它实现了 Quackable接口。把创建细节丢给工厂,而不是直接 new 创建,这也是控制反转的思想,在 Spring 框架中大量出现。
现在我们要创建带计数器叫声功能的鸭子工厂DuckCountFactory,它持有DuckFactory 的一个实例用于创建普通鸭子并放进 QuackCounter装饰器中从而得以创建带计数器的鸭子。这里其实也是用到了装饰器模式。
既然工厂我们定义好了,接下来就要修改屏幕模拟器代码运用工厂来产生鸭子。我们创建一个多态方法simulate(),此方法需要一个用来创建对象的工厂,传入不同的工厂则生产不同的产品家族。
我们把原来直接 new 产生鸭子的代码改造成通过工厂创建,代码如下。
组合模式-管理一群鸭子
需求方又来了,他们想控制不同鸭子群,把鸭子视为一个集合,为了满足能够管理不同鸭群的需求。
还记得么?组合模式允许我们像对待单个对象一样对待集合对象。所以利用组合模式管理一群Quackable再适合不过了。
首先,组合需要和叶子节点元素一样实现相同的接口。这里的「叶节点」就是Quackable。
我们用一个 ArrayList保存属于这个组合的Quackable对象,用add()方法新增Quackable对象到组合中。因为鸭群也是Quackable,所以也具备quack()方法,该方法会对整个鸭群产生作用。
我们的组合好了,现在要修改模拟器实现鸭群定义与管理,和之前一样。
1.首先创建所有的Quackable对象。
2.在创建不同的组合Flock鸭群,然后将对应的鸭子放入鸭群。
3.最后就能根据组合的鸭群分类测试鸭子满屏飞以及控制不同的鸭群行为了。
观察者模式-观察特定鸭子叫
组合模式让我们很好的管理鸭群,但是现在产品经理又有一个相反的需求:我们也需要追踪个别鸭子,当它呱呱叫鹅时候我们能够收到通知。
同学们是不是想到观察和模式,当感兴趣的某个事件发生的时候我们就收到通知。就像我们订阅的公众号发送消息,那么就通知。
被观察者QuackObservable
首先我们需要定义被观察者角色,提供「注册观察者」、「移除观察者」、「通知观察者」方法。任何想被观察的Quackable都要实现该接口,任何注册到QuackObservable的观察者QuackObserver都会收到呱呱叫通知,晒后我们会定义观察者。
现在我们需要把所有鸭子实现该接口成为被观察者,从而使得我们可以观察呱呱叫。所以我们干脆让Quackable来继承QuackObservable接口。我们必须确认所有实现Quackable的具体类能够扮演被观察者角色。
ObservableDelegate辅助类
由于所有的鸭子都实现了Quackable接口,而该接口又继承了QuackObservable被观察者接口。所以每个鸭子类都需要实现注册、通知、取消注册的代码。我们不这么干,抽出一个辅助类ObservableDelegate封装「注册」、「通知」、「取消注册」功能,然后和QuackObservable组合在一起,这样只需要一份代码即可,QuackObservable的所有调用都委托到ObservableDelegate辅助类。
在构造方法中我们传入QuackObservable,好让观察者知道是哪个对象在呱呱叫。
辅助类实现了所有必要的功能,我们只要把它插进一个类就可以将工作委托到 ObservableDelegate。
接下来我们整合 ObservableDelegate 和 Quackable使得 Quackable的注册于通知都委托到辅助类。所以每个Quackable的实现类都应该持有ObservableDelegate的实例。接下来我们改造鸭子实现类。
还有我们的适配器、以及鸭子叫计数器也是都把被观察者核心代码功能调用委托给辅助类
以及之前定义的鸭群,假如我们也想观察鸭群的叫通知,所以鸭群也要变成被观察者,同时将调用委托给
ObservableDelegate
观察者-QuackObserver
最后、我们要准备观察了,现在需要一些观察者。首先就从定义QuackObserver接口开始,它只有一个方法update(QuackObservable duck),传入的就是呱呱叫对象。
现在我们来实现一个观察者观察呱呱叫,当收到通知的时候我们就打印下是谁叫的。
屏幕模拟器
大功告成,我们只要在模拟器上创建观察者,并把观察者注册到感兴趣的被观察者中就实现了。
主要的改动就是
flockOfDucks.registerObserver(new Quackologist());创建观察者,并且观察 flockOfDucks这个鸭群的呱呱叫情况。
总结
最后我们用这个例子主要是打开读者的思维,遇到不同场景根据设计模式的特性便可以将问题迎刃而解,而且代码也变得优雅、高内聚低耦合,代码也得到了复用。
从一开始的鹅适配-「适配器模式」,再到为鸭子叫计数功能增强使用「装饰器模式」,接着创建很多鸭子。就想到了创建型模式「工厂模式」,为了管理鸭群-则想到了「组合模式」,最后想要观察特定鸭子叫,便很快的使用了观察者模式、同时还运用了「委托模式」使得注册观察的代码复用。
若对使用到的设计模式不熟悉可以阅读历史文章了解,欢迎大家提出意见以及指正。
文章转载自公众号:码哥字节
