抽象工厂模式 (Abstract Factory Pattern)

意图

抽象工厂模式是一种创建型设计模式,它提供一个创建一系列相关或依赖对象的接口,而无需指定它们具体的类。通过使用抽象工厂,客户端可以在不修改代码的情况下使用不同的产品系列。

问题

在现实世界中,考虑一个跨平台的图形用户界面(GUI)框架。该框架需要支持多个平台(如Windows、macOS、Linux),每个平台都有一组特定的控件(如按钮、文本框、复选框等)。如果在主应用中硬编码这些控件,会导致代码难以维护和扩展。

解决方案

使用抽象工厂模式,我们可以定义一个控件工厂接口和多个具体平台的控件工厂实现类,通过抽象工厂创建相关的控件对象,从而使得客户端代码与具体的平台无关。

模式结构

  1. 抽象产品(AbstractProduct):定义产品的接口,例如按钮、文本框等。
  2. 具体产品(ConcreteProduct):实现产品接口的具体产品类,例如Windows按钮、macOS按钮、Linux按钮等。
  3. 抽象工厂(AbstractFactory):声明创建一组相关产品对象的工厂方法。
  4. 具体工厂(ConcreteFactory):实现抽象工厂接口的具体工厂类,负责实例化具体的产品对象。

代码

以下是使用Go语言实现的抽象工厂模式示例:

package main

import "fmt"

// Button 是按钮的抽象产品接口
type Button interface {
    Render() string
}

// TextBox 是文本框的抽象产品接口
type TextBox interface {
    Render() string
}

// WindowsButton 是具体产品 - Windows 按钮
type WindowsButton struct{}

func (wb *WindowsButton) Render() string {
    return "Rendering Windows Button"
}

// WindowsTextBox 是具体产品 - Windows 文本框
type WindowsTextBox struct{}

func (wtb *WindowsTextBox) Render() string {
    return "Rendering Windows TextBox"
}

// MacOSButton 是具体产品 - macOS 按钮
type MacOSButton struct{}

func (mb *MacOSButton) Render() string {
    return "Rendering macOS Button"
}

// MacOSTextBox 是具体产品 - macOS 文本框
type MacOSTextBox struct{}

func (mtb *MacOSTextBox) Render() string {
    return "Rendering macOS TextBox"
}

// LinuxButton 是具体产品 - Linux 按钮
type LinuxButton struct{}

func (lb *LinuxButton) Render() string {
    return "Rendering Linux Button"
}

// LinuxTextBox 是具体产品 - Linux 文本框
type LinuxTextBox struct{}

func (ltb *LinuxTextBox) Render() string {
    return "Rendering Linux TextBox"
}

// GUIFactory 是抽象工厂接口
type GUIFactory interface {
    CreateButton() Button
    CreateTextBox() TextBox
}

// WindowsFactory 是具体工厂类 - Windows 工厂
type WindowsFactory struct{}

func (wf *WindowsFactory) CreateButton() Button {
    return &WindowsButton{}
}

func (wf *WindowsFactory) CreateTextBox() TextBox {
    return &WindowsTextBox{}
}

// MacOSFactory 是具体工厂类 - macOS 工厂
type MacOSFactory struct{}

func (mf *MacOSFactory) CreateButton() Button {
    return &MacOSButton{}
}

func (mf *MacOSFactory) CreateTextBox() TextBox {
    return &MacOSTextBox{}
}

// LinuxFactory 是具体工厂类 - Linux 工厂
type LinuxFactory struct{}

func (lf *LinuxFactory) CreateButton() Button {
    return &LinuxButton{}
}

func (lf *LinuxFactory) CreateTextBox() TextBox {
    return &LinuxTextBox{}
}

// main 函数演示了抽象工厂模式的使用
func main() {
    var factory GUIFactory

    // 使用 Windows 工厂
    factory = &WindowsFactory{}
    button := factory.CreateButton()
    textBox := factory.CreateTextBox()
    fmt.Println(button.Render())
    fmt.Println(textBox.Render())

    // 使用 macOS 工厂
    factory = &MacOSFactory{}
    button = factory.CreateButton()
    textBox = factory.CreateTextBox()
    fmt.Println(button.Render())
    fmt.Println(textBox.Render())

    // 使用 Linux 工厂
    factory = &LinuxFactory{}
    button = factory.CreateButton()
    textBox = factory.CreateTextBox()
    fmt.Println(button.Render())
    fmt.Println(textBox.Render())
}

适用场景

  • 需要创建一系列相关或互相依赖的对象时。
  • 系统需要独立于产品的创建和组合方式时。
  • 需要提供一个产品类库,且只显示它们的接口而不是实现时。

实现方式

  1. 创建抽象产品接口,定义产品的通用行为。
  2. 创建具体产品类,实现抽象产品接口。
  3. 创建抽象工厂接口,声明创建产品对象的工厂方法。
  4. 创建具体工厂类,实现抽象工厂接口,负责实例化具体的产品对象。

优缺点

优点

  • 分离了具体类的创建,使得代码更容易维护和扩展。
  • 符合单一职责原则,工厂类只负责对象的创建。
  • 遵循开闭原则,可以通过添加新的具体工厂和具体产品类来扩展系统,而不修改已有代码。

缺点

  • 每增加一组产品类,就需要增加相应的具体工厂类,增加了代码量。

其他模式的关系

  • 工厂方法模式(Factory Method Pattern):抽象工厂模式通常与工厂方法模式一起使用,具体工厂类可以使用工厂方法来创建具体产品对象。
  • 单例模式(Singleton Pattern):具体工厂类可以使用单例模式来确保每个具体工厂只有一个实例。
  • 依赖注入(Dependency Injection):抽象工厂模式可以与依赖注入一起使用,将具体工厂对象注入到客户端代码中。