Kotlin设计模式-抽象工厂

725 阅读6分钟

1、介绍

抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供了一种创建一系列相关或相互依赖对象的方式,而消耗指定其具体类。该模式通过定义一个接口(抽象工厂)来声明一组方法,每个方法用于创建不同类型的相关对象。具体工厂类实现这个接口,并实现其中的创建方法,从而在运行时动态地创建创建合适的工厂来对象。

以下是抽象工厂模式的主要参与角色:

  1. 抽象工厂(Abstract Factory): 声明一组用于创建不同类型对象的抽象方法。这是一个接口或者抽象类,定义了可以创建不同产品类别的方法。
  2. 具体工厂(Concrete Factory): 实现抽象工厂接口,实现其中的抽象方法,从而创建特定类型的具体对象。每个具体工厂负责一组产品族的创建。
  3. 抽象产品(Abstract Product): 声明一组产品对象的抽象方法。这些产品对象通常都属于同一个产品类别,但具体类型未知。
  4. 产品(具体产品): 实现抽象产品接口,定义具体的产品类型。每个具体工厂负责创建一组具体产品。

2、优点

  • 分离了具体类的创建,客户端代码与具体类的解耦。
  • 能够确保创建的一组相关对象是一致的,避免了不一致性。
  • 支持新增产品类别的扩展,只需要新增对应的具体工厂类和产品类即可。

3、限制

  • 难以支持新增产品等级结构的扩展,因为每个具体工厂只负责一组产品类别。
  • 增加新的产品种类会导致抽象工厂和所有具体工厂的修改,可能造成关闭原则的破坏。
  • 系统的复杂性增加了,特别是当存在多个产品类别和产品等级结构时。

4、适用

从优点和注意事项中我们也能看出,抽象工厂模式用于解决以下问题:

  1. 创建一组相关对象: 当您需要一组相关或相互依赖的对象时,抽象工厂模式能够帮助您一次性创建这些对象,确保它们之间的关联性和一致性。
  2. 隐藏具体类的创建细节: 抽象工厂模式将具体类的创建细节封装在工厂类中,使客户端代码需要了解具体类的实现细节,从而降低了系统的连接度。
  3. 产品类别和产品等级结构: 抽象工厂模式适用于存在多个产品类别和产品等级结构的情况。支持每个具体工厂负责一个产品类别,而每个具体产品对应一个产品等级。
  4. 在运行时允许选择工厂: 抽象工厂模式在运行时根据实际需求合适的具体工厂,从而创建选择需要的对象。
  5. 遵循开闭原则: 抽象工厂模式支持在不修改现有代码的情况下扩展新的产品类别和产品等级结构,符合开闭原则。
  6. 提供一致性接口: 抽象工厂模式保证不同产品族之间的一致性接口,使得这些产品族能够在一定的接口上交互和兼容。
  7. 代码组织和管理: 抽象工厂模式能够将创建逻辑和产品实现从客户端代码中分离出来,提高代码的组织性和可维护性。

5、代码

示例:UI组件库

假设正在开发一个Android应用程序,需要根据不同的主题(产品族)以及不同的UI组件类型(产品等级结构)来渲染UI。可以使用抽象工厂模式来实现这个功能。

 // 抽象产品族 - 按钮
 interface Button {
     fun render(): String
 }
 ​
 // 具体产品族 - 浅色主题按钮
 class LightButton : Button {
     override fun render(): String {
         return "Rendered a light-themed button"
     }
 }
 ​
 // 具体产品族 - 深色主题按钮
 class DarkButton : Button {
     override fun render(): String {
         return "Rendered a dark-themed button"
     }
 }
 ​
 // 抽象产品族 - 文本框
 interface TextBox {
     fun render(): String
 }
 ​
 // 具体产品族 - 浅色主题文本框
 class LightTextBox : TextBox {
     override fun render(): String {
         return "Rendered a light-themed text box"
     }
 }
 ​
 // 具体产品族 - 深色主题文本框
 class DarkTextBox : TextBox {
     override fun render(): String {
         return "Rendered a dark-themed text box"
     }
 }
 ​
 // 抽象工厂
 interface UiComponentFactory {
     fun createButton(): Button
     fun createTextBox(): TextBox
 }
 ​
 // 具体工厂 - 浅色主题工厂
 class LightThemeFactory : UiComponentFactory {
     override fun createButton(): Button {
         return LightButton()
     }
 ​
     override fun createTextBox(): TextBox {
         return LightTextBox()
     }
 }
 ​
 // 具体工厂 - 深色主题工厂
 class DarkThemeFactory : UiComponentFactory {
     override fun createButton(): Button {
         return DarkButton()
     }
 ​
     override fun createTextBox(): TextBox {
         return DarkTextBox()
     }
 }

在这个例子中,ButtonTextBox分别是抽象产品族,LightButtonDarkButtonLightTextBoxDarkTextBox是其具体实现。

UiComponentFactory是抽象工厂,有两个具体实现:LightThemeFactoryDarkThemeFactory,分别用于创建浅色和深度学习主题的组件UI。

使用抽象工厂模式,您可以在运行时选择使用哪种主题和 UI 组件类型。这样,您可以保持不同主题下 UI 组件之间的一致性,同时还能够轻松地添加新的主题和 UI 组件类型。

还可以继续封装,并利用Kotlin的语法糖(当然前面的Box和Button也可以如此改动)

 abstract class UiComponentFactorySpec {
     abstract fun create(): UiComponentFactory
 ​
     companion object {
         inline fun <reified T : UiComponentFactory> createFactory(): UiComponentFactory = when (T::class) {
             LightThemeFactory::class -> LightThemeFactory()
             LightThemeFactory::class -> LightThemeFactory()
             else -> throw IllegalArgumentException()
         }
     }
 }

(代码不代表着只能这么做,而是可以这么做)

6、结尾

在Android源码中,虽然可能没有直接命名“抽象工厂模式”,但很多地方都使用了类似的设计原则来实现多个产品族和产品等级结构之间的关系。以下是一些例子,这些示例可能没有直接称为抽象工厂模式,但它们展示了类似的设计思想:

  1. LayoutInflater并且ViewFactory Android中的LayoutInflater充当了一个创建视图的工厂。它负责将布局文件转换为实际的视图层次结构。通过不同的实现,您可以定制不同主题下的视图,类似于ViewFactory抽象工厂模式。
  2. 主题资源允许: Android的资源系统您可以为不同的主题定义不同的资源,如布局、颜色、样式等。这种分组方式类似于抽象工厂中的产品类别和产品等级结构。
  3. BitmapFactory Android 中的BitmapFactory类提供了不同的方式来解码图,例如从资源、文件或网络。虽然没有显式使用抽象工厂模式,但其设计思想与抽象工厂类似,可以根据不同的情况选择不同的工厂方法来创建角色图。
  4. Notification.Builder并且NotificationCompat.Builder 在Android中,通知的创建可以使用Notification.Builder或者NotificationCompat.Builder,它们分别适用于不同的Android版本。这种方式允许根据兼容性需求选择不同的创建方法,类似于抽象工厂模式。

尽管在 Android 源码中可能没有直接的抽象工厂模式的实现,但许多设计原则和思想与抽象工厂模式紧密相关,用于管理多个产品族和产品等级结构之间的关系。

选择适合场景的设计模式是一个需要谨慎权衡的过程。 代码在这里GitHub

7、感谢

  1. Design-Patterns-In-Kotlin
  2. Refactoringguru
  3. Java-Design-Patterns