设计模式之抽象工厂模式 意图 抽象工厂模式 (Abstract Factory)是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。
**优点 **
抽象工厂模式隔离了具体类的生成 ,用户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象 。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开放封闭原则” 。
缺点
在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
适用场景 抽象工厂模式适用场景:
一个系统要独立于它的产品的创建、组合和表示时。
一个系统要由多个产品系列中的一个来配置时。
当你要强调一系列相关的产品对象的设计以便进行联合使用时。
当你提供一个产品类库,而只想显示它们的接口而不是实现时。
结构
结构说明
抽象产品 (Abstract Product) 为构成系列产品的一组不同但相关的产品声明接口。
具体产品 (Concrete Product) 是抽象产品的多种不同类型实现。 所有变体 (维多利亚/现代) 都必须实现相应的抽象产品 (椅子/沙发)。
抽象工厂 (Abstract Factory) 接口声明了一组创建各种抽象产品的方法。
具体工厂 (Concrete Factory) 实现抽象工厂的构建方法。 每个具体工厂都对应特定产品变体, 且仅创建此种产品变体。
尽管具体工厂会对具体产品进行初始化, 其构建方法签名必须返回相应的抽象 产品。 这样, 使用工厂类的客户端代码就不会与工厂创建的特定产品变体耦合。 客户端 (Client) 只需通过抽象接口调用工厂和产品对象, 就能与任何具体工厂/产品变体交互。
结构代码范式 【AbstractProduct】
声明一个接口,这个接口中包含产品对象类型。
1 2 3 4 5 6 7 abstract class AbstractProductA { public abstract void show () ; } abstract class AbstractProductB { public abstract void show () ; }
【ConcreteProduct】
定义一个产品对象,这个产品对象是由相关的具体工厂创建的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class ConcreteProductA1 extends AbstractProductA { @Override public void show () { System.out.println("ConcreteProductA1" ); } } class ConcreteProductA2 extends AbstractProductA { @Override public void show () { System.out.println("ConcreteProductA2" ); } } class ConcreteProductB1 extends AbstractProductB { @Override public void show () { System.out.println("ConcreteProductB1" ); } } class ConcreteProductB2 extends AbstractProductB { @Override public void show () { System.out.println("ConcreteProductB2" ); } }
【AbstractFactory】
声明一个接口,这个接口中包含创建抽象产品对象的方法。
1 2 3 4 abstract class AbstractFactory { public abstract AbstractProductA createProductA () ; public abstract AbstractProductB createProductB () ; }
【ConcreteFactory】
实现创建具体产品对象的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class ConcreteFactory1 extends AbstractFactory { @Override public AbstractProductA createProductA () { return new ConcreteProductA1 (); } @Override public AbstractProductB createProductB () { return new ConcreteProductB1 (); } } class ConcreteFactory2 extends AbstractFactory { @Override public AbstractProductA createProductA () { return new ConcreteProductA2 (); } @Override public AbstractProductB createProductB () { return new ConcreteProductB2 (); } }
【客户端】
只使用 AbstractFactory
和 AbstractProduct
声明的接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class AbstarctFactoryPattern { public static void main (String[] args) { AbstractFactory factory1 = new ConcreteFactory1 (); AbstractProductA productA1 = factory1.createProductA(); AbstractProductB productB1 = factory1.createProductB(); productA1.show(); productB1.show(); AbstractFactory factory2 = new ConcreteFactory2 (); AbstractProductA productA2 = factory2.createProductA(); AbstractProductB productB2 = factory2.createProductB(); productA2.show(); productB2.show(); } }
【输出】
1 2 3 4 ConcreteProductA1 ConcreteProductB1 ConcreteProductA2 ConcreteProductB2
伪代码 下面例子通过应用抽象工厂 模式, 使得客户端代码无需与具体 UI 类耦合, 就能创建跨平台的 UI 元素, 同时确保所创建的元素与指定的操作系统匹配。
跨平台应用中的相同 UI 元素功能类似, 但是在不同操作系统下的外观有一定差异。 此外, 你需要确保 UI 元素与当前操作系统风格一致。 你一定不希望在 Windows 系统下运行的应用程序中显示 macOS 的控件。
抽象工厂接口声明一系列构建方法, 客户端代码可调用它们生成不同风格的 UI 元素。 每个具体工厂对应特定操作系统, 并负责生成符合该操作系统风格的 UI 元素。
其运作方式如下: 应用程序启动后检测当前操作系统。 根据该信息, 应用程序通过与该操作系统对应的类创建工厂对象。 其余代码使用该工厂对象创建 UI 元素。 这样可以避免生成错误类型的元素。
使用这种方法, 客户端代码只需调用抽象接口, 而无需了解具体工厂类和 UI 元素。 此外, 客户端代码还支持未来添加新的工厂或 UI 元素。
这样一来, 每次在应用程序中添加新的 UI 元素变体时, 你都无需修改客户端代码。 你只需创建一个能够生成这些 UI 元素的工厂类, 然后稍微修改应用程序的初始代码, 使其能够选择合适的工厂类即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 interface GUIFactory is method createButton () :Button method createCheckbox () :Checkbox class WinFactory implements GUIFactory is method createButton () :Button is return new WinButton () method createCheckbox () :Checkbox is return new WinCheckbox () class MacFactory implements GUIFactory is method createButton () :Button is return new MacButton () method createCheckbox () :Checkbox is return new MacCheckbox () interface Button is method paint () class WinButton implements Button is method paint () is class MacButton implements Button is method paint () is interface Checkbox is method paint () class WinCheckbox implements Checkbox is method paint () is class MacCheckbox implements Checkbox is method paint () is class Application is private field factory: GUIFactory private field button: Button constructor Application (factory: GUIFactory) is this .factory = factory method createUI () is this .button = factory.createButton() method paint () is button.paint() class ApplicationConfigurator is method main () is config = readApplicationConfigFile() if (config.OS == "Windows" ) then factory = new WinFactory () else if (config.OS == "Mac" ) then factory = new MacFactory () else throw new Exception ("错误!未知的操作系统。" ) Application app = new Application (factory)
案例 众所周知,苹果和三星这两家世界级的电子产品厂商都生产手机和电脑。
我们以生产手机和电脑为例,演示一下抽象工厂模式的应用
【AbstractProduct 角色】
首先,定义手机和电脑两个抽象接口,他们都有各自的产品信息。
1 2 3 4 5 6 7 interface Telephone { public String getProductInfo () ; } interface Computer { public String getProductInfo () ; }
【ConcreteProduct 角色】
ConcreteProduct
根据 AbstractProduct
来定义具体的产品属性、方法。
在我们的例子中,苹果、三星两家公司的手机和电脑都有各自的具体产品信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class AppleTelephone implements Telephone { @Override public String getProductInfo () { return "苹果手机,采用ios系统" ; } } class SamsungTelephone implements Telephone { @Override public String getProductInfo () { return "三星手机,采用android系统" ; } } class AppleComputer implements Computer { @Override public String getProductInfo () { return "苹果电脑,采用mac系统" ; } } class SamsungComputer implements Computer { @Override public String getProductInfo () { return "三星电脑,采用windows系统" ; } }
【AbstractFactory 角色】
苹果,三星这两个厂商都生产手机和电脑。所以它们可以有一个抽象父类或父接口,提供生产手机和生产电脑的方法。
1 2 3 4 5 6 interface ElectronicFactory { public Telephone produceTelephone () ; public Computer produceComputer () ; }
【ConcreteFactory 角色】
苹果、三星工厂分别实现父接口,生产不同类型的产品。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class AppleFactory implements ElectronicFactory { @Override public Telephone produceTelephone () { return new AppleTelephone (); } @Override public Computer produceComputer () { return new AppleComputer (); } } class SamsungFactory implements ElectronicFactory { @Override public Telephone produceTelephone () { return new SamsungTelephone (); } @Override public Computer produceComputer () { return new SamsungComputer (); } }
【客户端】
1 2 3 4 5 6 7 8 9 public class PhoneFactoryDemo { public static void main (String[] args) { ElectronicFactory appleFactory = new AppleFactory (); Telephone phone = appleFactory.produceTelephone(); System.out.println(phone.getProductInfo()); Computer computer = appleFactory.produceComputer(); System.out.println(computer.getProductInfo()); } }
【输出】
1 2 苹果手机,采用ios系统 苹果电脑,采用mac 系统
与其他模式的关系
在许多设计工作的初期都会使用工厂方法模式 (较为简单, 而且可以更方便地通过子类进行定制), 随后演化为使用抽象工厂模式 、 原型模式 或生成器模式 (更灵活但更加复杂)。
生成器 重点关注如何分步生成复杂对象。 抽象工厂 专门用于生产一系列相关对象。 抽象工厂 会马上返回产品, 生成器 则允许你在获取产品前执行一些额外构造步骤。
抽象工厂模式 通常基于一组工厂方法 , 但你也可以使用原型模式 来生成这些类的方法。
当只需对客户端代码隐藏子系统创建对象的方式时, 你可以使用抽象工厂 来代替外观模式 。
你可以将抽象工厂 和桥接模式 搭配使用。 如果由桥接 定义的抽象只能与特定实现合作, 这一模式搭配就非常有用。 在这种情况下, 抽象工厂 可以对这些关系进行封装, 并且对客户端代码隐藏其复杂性。
抽象工厂 、 生成器 和原型 都可以用单例模式 来实现。
参考资料