目的
Abstract Factory是创建模式的一种,让你在没有指定具体类的情况下生产相关对象的系列。
问题
假设你在写一个家具店的模拟器。你的代码由以下构成:
相关产品的系列,像:Chair + Sofa + CoffeeTable。
系列的几个变种。比如,产品Chair + Sofa + Coffee + CoffeeTable可以有这些变种:IKEA,VictorianStyle,ArtDeco。
你需要有种方式创建个性化的家具对象,以便它们能够匹配相同系列中的其他对象。在没有匹配到家具时,客户会感到失望。
另外,你不想在添加新产品或者产品系列时改变已经存在的代码。家具供应商常常会更新他们的目录,并且你不想每次在供货商改目录时修改你的核心代码。
解决
第一,Abstract Factory模式建议遍历所有不同的产品并且强制这些变种遵循通用的接口。比如,所有的椅子变种必须遵循Chair接口;所有的咖啡桌必须遵循CoffeeTable接口。
第二步,创建AbstractFactory,一个基础接口,声明了创建产品系列的所有产品的方法(比如:createChair,createSofa和createCoffeeTable)。这步最重要的一件事就是让这些方法返回代表抽象产品类型的接口:Chair,Sofa,CoffeeTable。
第三步,实现具体的工厂。工厂是返回某种特定产品的类。比如,IKEAFactory,将只会返回IKEAChair,IKEASofa和IKEACoffeeTable对象。所有的工厂在创建同一个变种的产品时必须遵循AbstractFactory接口。
客户端代码只能通过抽象接口同工厂和产品协作。这样你就可以通过传递不同的工厂对象修改要用的产品类型。
所以,当客户端像工厂请求创建一把椅子时,它不必关心工厂的具体类。也不必关心它将得到椅子的具体类。不管它将得到一把时尚的IKEA模型还是一把Victorian风格的椅子,它都用使用抽象的Chair接口和椅子协作。客户端代码只需要知道椅子实现了在接口中声明的sit方法。它还知道不管返回哪种椅子,它的类型是和沙发还有咖啡桌匹配的,因为它们是同一个工厂创建的。
好了,但是谁创建实际的工厂对象呢?通常,程序在初始化阶段创建一个具体的工厂,并且工厂的类型依赖配置或者环境。
结构
Abstract products为创建产品系列的所有不同产品声明接口。通常,有几个不同的产品接口。
Concrete products实现不同Abstract product接口。实现相同接口的具体产品集合代表一个系列的不同变种。
Abstract factory声明了创建系列中所有产品的接口。
Concrete factory实现了abstract factory的创建方法。每个具体工厂代表一个系列产品的特定变种。
虽然具体的工厂实例化具体的产品,但是创建方法的签名必须声明为相应抽象产品类型。
通过这种歌方式,客户端代码在使用工厂时就不会和具体的产品变种耦合。它就能通过使用抽象接口和任何工厂/产品协作。
伪代码
这个例子用来说明Abstract Factory模式可一用来创建跨平台的UI而不需要客户端代码和具体UI类耦合。
客户端代码从工厂中请求各个UI元素。返回元素的具体类型取决于客户端代码传递的工厂类型。客户端代码通过抽象接口和元素协作。只要它使用相同的工厂对象,它所有的产品都是兼容的。
Abstract Factory模式使得客户端代码和具体UI元素类独立。另外,当添加一个新的UI变种时,你不需要修改已经存在的代码(比如,实现Linux的UI元素)。你只需要创建一个工厂的新子类,让他返回新类型的UI元素。
1 | // This pattern assumes that you have several families of products, structured |
适用性
当业务逻辑必须要和同一个产品系列的不同变种协作,并且你不想依赖具体产品类时(或者它们无法预先知道)。
Abstract Factory对客户端代码隐藏了创建产品类的信息。客户端代码可以和任何工厂创建出来的任何产品协作,只要客户端通过抽象接口和它们交互。
当一个类拥有多个Factory Method使得它的主要责任不明确时。
每个类专注做一件事是很好的程序设计。当一个类需要处理多个产品类型,它就应该使用一个独立的抽象工厂来替代多个工厂方法。
如何实现
画出不同产品和相同产品不同变种的矩阵。
为所有不同产品类型创建抽象接口并且让所有具体产品遵循这些接口。
声明抽象工厂接口。这个接口应该列出所有不同类型产品的创建方法。
为产品系列的每个变种实现不同的工厂类。
创建一个空场的初始化代码写在客户端中。客户端依赖配置或者当前环境中来决定需要的工厂的类型并创建出来。
在客户端代码中,把所有调用产品构造方法的地方替换成调用工厂的创建方法。
优点
符合开闭原则。
允许构建系列产品对象并担保兼容性。
避免具体产品和使用它们的代码的耦合。
在多个类间隔离责任。
缺点
- 创建多个额外类,增加代码整体复杂度。
和其他模式的关系
通常,设计从使用Factory Method开始(比较简单,并且可以通过子类实现定制),逐渐演变到Abstract Factory,Prototype,或者Builder(更加复杂,但更灵活),因为设计者发现它们需要更灵活的程序。
Builder关注点在一步一步的构造出一个复杂对象。Abstract Factory创建产品对象的系列(不管是简单的还是复杂的)。Builder在最后一步返回产品,但是Abstrct Factory立刻返回结果。
Abstract Factory类通常用Factory Method来实现,单丝它们也可以用Prototype来实现。
Abstract Factory可以用来代替Facade隐藏特定平台的类。
Abstract Factory可以和Bridge模式单独使用。当Bridge的“接口”部分只能与特定的“实现”一起工作时非常有用。这种情况下,工厂能够封装这些关系并且对客户端隐藏复杂性。
Abstract Factory,Builder和Prototype都可以实现为Singleton。
小结
Abstract Factory是创建型模式的一种,用来解决创建没有制定具体类的产品系列。
Abstract Factory定义了一个创建所有不同产品的接口,但是把创建真正产品的实现放在具体工厂类中。美国工厂类型都代表一个某些产品的变种。
客户端代码不是直接调用构造方法(new操作)而是调用一个工厂对象的创建方法来创建对象。因为一个工厂代表了一个产品的变种,它的产品都是兼容的。
客户端只通过抽象接口和工厂还有产品协作。它允许相同的客户端代码和不同的产品协作。你只需要创建一新的具体工厂类,并把它传递给客户端代码就行。如果你分不清楚Factories,Factory Method和Abstract Factory,可以阅读工厂比较指南(待译)。
Java的模式的使用
用例:许多框架和类库采用抽象工厂模式来提供扩展和定制它们的标准组件。
Java的核心包中应用如下:
javax.xml.parsers.DocumentBuilderFactory#newInstance()
javax.xml.transform.TransformerFactory#newInstance()
javax.xml.xpath.XPathFactory#newInstance()
鉴定:该模式很容易通过返回工厂对象的方法来识别。然后,工厂用于创建特定的子组件。
参考
翻译整理自:https://refactoring.guru/design-patterns/abstract-factory