Aha!设计模式(3)-抽象工厂(2)

动机

 

《设计模式》引言中对于动机的说明如下:

 

用以说明一个设计问题以及如何用模式中的类、对象来解决该问题的特定情景。该情景会帮助你理解随后对模式更抽象的描述。

 

简单地说:就是设计模式的应用实例。

 

回到抽象工厂模式的动机。

 

考虑一个支持多种视感(look-and-feel)标准的用户界面工具包,例如 Motif和Presentation Manager。不同的视感风格为诸如滚动条、窗口和按钮等用户界面“窗口组件”定义不同的外观和行为。为保证视感风格标准间的可移植性,一个应用不应该 为一个特定的视感外观硬编码它的窗口组件。在整个应用中实例化特定视感风格的窗口组件类将使得以后很难改变视感风格

 

《设 计模式》成书很早,所以书中会出现许多过去的软件和技术,例如上文Moti和Presentation Manager。这里建议大家不必过分在意这些名词,能够理解作者意图即可。上面的文字中忽略例如的部分也不会有任何问题,只要能找到类似的场景就好,例 如Linux操作系统的主题就是一个不错的例子。除了表示方式不同之外,动作方式也会有所不同。可以想象:如果将所有和主题相关的代码都写在一个类里,代 码势必会变得很复杂,最后的结果就是得无法维护和扩展。

 

为解决这一问题我们可以定义一个抽象的 WidgetFactory类,这个类声明了一个用来创建每一类基本窗口组件的接口。每一类窗口组件都有一个抽象类,而具体子类则实现了窗口组件的特定视 感风格。对于每一个抽象窗口组件类, Wi dgetFactory接口都有一个返回新窗口组件对象的操作。客户调用这些操作以获得窗口组件实例,但客户并不知道他们正在使用的是哪些具体类。这样客 户就不依赖于一般的视感风格,如下页图所示。

Aha!设计模式(3)-抽象工厂(2)

 

每 一种视感标准都对应于一个具体的 WidgetFactory子类。每一子类实现那些用于创建合适视感风格的窗口组件的操作。例如, MotifWidgetFactory的Crea teScrollBar操作实例化并返回一个Motif滚动条,而相应的PMWidgetFactory操作返回一个Presentation Manager的滚动条。客户仅通过WidgetFactory接口创建窗口组件,他们并不知道哪些类实现了特定视感风格的窗口组件。换言之,客户仅与抽 象类定义的接口交互,而不使用特定的具体类的接口。

 

面 向对象中解决这类一问题的方法只有一个,就是利用多态,首先建立各种窗口组建的抽象类,然后在每种主题中为相应的组建准备具象类。上图中的Windows 和ScrollBar就是窗口组建的抽象类,而PMWindow,MotiWindow,PMSrollBar,MotiScrollBar就是对应的具 象类。

 

对于窗口组建的利用者Client来说,并不需要知道各个窗口组建的具象类,只有知道组件是某种Window或者某种ScrollBar即可。但是还有一个问题:Client在使用组件时虽然可以不知道组件具象类信息,但构建组件的时候无论如何也没有办法继续装糊涂。

 

解决这个问题想法就是将构建窗口组件的部分封装的到一个类中。名字可以叫WidgetFacroty,它根据主题信息生成对应的窗口组件,坏事交给别人做。这就是简单工厂模式。简单工程模式依然不够完美:因为当增加主题的时候,仍然需要对工厂类进行一定的修改。

 

终于到了主角出场的时候了。

 

这里继续请出面向对象的绝招:多态。我们可以准备一个工厂的抽象类,然后为每种主题准备具象类。每种具象工厂类负责生成对应主题的窗口组件。在需要更换主题时,更换具象工厂类即可。

 

WidgetFactory也增强了具体窗口组件类之间依赖关系。一个 Motif的滚动条应该与Motif按钮、Motif正文编辑器一起使用,这一约束条件作为使用 MotifWidgetFactory的结果被自动加上。

 

Client得到窗口组件的方式只有通过指定的具象工厂类一种方式,这样就从结果上决定了哪些具象窗口组建类一起协同工作。

 

作业题解答

 

前一篇文章的最后,留了一个尾巴,关于依赖关系的画法的。其实这本不应该一个问题。因为面向对象的一个基本原则就是降低耦合以增加可维护性和可扩展性,所以要尽量对抽象类编程,最好只和抽象类有单向的依赖关系。指向具象类的依赖虽然不能完全回避,但这种关联线最好只有一条。否则就很有可能是那种"长得就犯法“的设计。

 

注:

本文中蓝色粗体文字都引自《设计模式》一书。

 

觉得本文有帮助?请分享给更多人。

阅读更多更新文章,请扫描下面二维码,关注微信公众号【面向对象思考】

Aha!设计模式(3)-抽象工厂(2)