抽象工厂模式(Abstract Factory Pattern)是另外一种工厂模式,人们经常会将它与工厂方法模式搞混,实际上它们是完全不一样的。抽象工厂模式提供一个接口用于创建相关类似对象。
问题提出
假设我们要创建一个图形框架。在我们的框架里,有Button、TextBox、Drop-down list等部件。我们希望这些部件支持不同的主题样式,比如Material Design,比如Ant等等。
当我们选择某个主题时,我们框架的所有部件都有一致的样式。比如我们选择Material Design,我们将不会看到Ant样式,所有的部件都呈现出Material Design的样式。
下面编写代码模拟。
创建接口Widget,它定义了render方法。
创建接口Button和TextBox,它们均继承于接口Widget。Button和TextBox用接口表示,是因为它们的具体实现有两个主题样式:Material Design和Ant。
为组织代码,我们创建包material,用于容纳material样式的MaterialButton和MaterialTextBox。它们分别实现了接口Button和TextBox。
同样的,创建Ant包,它包含AntButton类和AntTextBox类。
现在,我们的应用程序准备使用这个图形框架。但在此之前,我们先创建枚举类型Theme。
创建ContactForm类,方法render接收一个Theme枚举值,根据该枚举值动态渲染部件的主题样式。
代码有些问题:
首先,它违反了开放闭合原则。明天如果增加一个新的主题样式,我们就得回到ContactForm类修改render方法。
其次,我们必须非常小心地使用相对应的类,不能搞错,比如当枚举值为Theme.ANT时,我们不小心使用了MaterialTextBox类,将会出现我们不希望出现的结果。
使用抽象工厂模式可以解决这些问题。
解决方案
我们来讨论抽象工厂模式(Abstract Factory Pattern)。
首先你得知道工厂方法(Factory Method)是一个方法,正如它的名字一样。而抽象工厂(Abstract Factory)是一个抽象,它是创建相关类似对象的一个接口。
它们是这样工作的。
如上图,WidgetFactory是一个接口,定义了方法createButton和createTextBox。每一个方法都是工厂方法(Factory Method),而WidgetFactory本身是一个抽象,它是一个接口。
我们可以创建WidgetFactory接口的具体实现,比如MaterialWidgetFactory和AntWidgetFactory。MaterialWidgetFactory仅知道如何创建Material主题样式的部件(Widgets),同样AntWidgetFactory也仅知道如何创建Ant主题的部件。
这个就被我们称为抽象工厂模式。
四人帮经典的结构图示如下。
代码实现
创建接口WidgetFactory,它定义了createButton和createTextBox方法。
对于不同的主题样式,我们创建具体不同的实现。首先是实现Material主题的部件工厂。
在material包下,创建MaterialWidgetFactory类,它实现了WidgetFactory接口。
类似地,实现AntWidgetFactory类。
现在到了有趣的部分,我们可以去掉ContactForm类render方法里的丑陋的判断代码,render方法的参数由枚举值Theme改成WidgetFactory接口。render实现也非常简单,调用参数factory的createButton和createTextBox方法创建相应主题样式的Button和TextBox,然后进行渲染。
现在的代码很简洁,非常棒。这种实现遵循了开放闭合原则,明天我们如果添加新主题(Theme),我们不必回头修改ContactForm类,我们只需创建新主题样式,并传递给render即可。
我们到Main中进行测试。
当我们传递给render的factory对象是MaterialWidgetFactor时,则渲染出Material主题样式的部件。
而当我们传递AntWidgetFactor时,则渲染出Ant主题样式。
使用这种模式,我们不必根据选择的主题而记住使用相应的部件,我们不会搞混一个Ant Button和Material TextBox。
再一次说明,抽象工厂提供了创建类似相关对象的一个接口。
但如果您的应用程序里没有这种场景,您不必使用抽象工厂模式。
小结
抽象工厂模式提供了一个接口,用于创建相关类似对象。