Menu
CoddyTech

抽象工厂模式

本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 88 课,共 107 课。

抽象工厂模式扩展了工厂模式,可以创建相关对象族,而无需指定它们的具体类型。你不再使用单个工厂函数,而是定义一个工厂接口,由多个具体工厂来实现,每个工厂都会生成一组彼此相关且一致的对象。

假设有一个 UI 工具包,需要创建按钮和复选框。不同的操作系统需要不同的实现方式,但这些组件必须保持一致地协同工作:

type Button interface {
    Render() string
}

type Checkbox interface {
    Check() string
}

// 抽象工厂接口
type GUIFactory interface {
    CreateButton() Button
    CreateCheckbox() Checkbox
}

每个具体工厂都会生产一组相关组件:

// Windows 产品族
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }

type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }

type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }

// Mac 产品族
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }

type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }

type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }

客户端代码通过工厂接口进行操作,不依赖具体实现:

func BuildUI(factory GUIFactory) {
    button := factory.CreateButton()
    checkbox := factory.CreateCheckbox()
    fmt.Println(button.Render(), checkbox.Check())
}

当系统需要处理多个相关产品系列,并且你希望确保同一系列的组件搭配使用时,可以使用抽象工厂模式。它比简单工厂更复杂,所以只有在确实需要管理产品系列时才使用它。

challenge icon

挑战

简单

让我们使用抽象工厂模式来构建一个家具店系统吧!你将创建能够生产相关家具系列的工厂:每个工厂都会生产风格一致的家具(现代或复古),确保同一家工厂生产的椅子和桌子在外观上相配。

你将把代码分成三个文件:

  • furniture.go:定义产品接口和具体的家具类型。

    创建两个产品接口:

    • Chair,包含方法 SitOn() string
    • Table,包含方法 PlaceItems() string

    实现两个家具系列:

    • 现代系列:ModernChair 返回 Sitting on a sleek modern chair,ModernTable 返回 Placing items on a glass modern table
    • 复古系列:VintageChair 返回 Sitting on an ornate vintage chair,VintageTable 返回 Placing items on a wooden vintage table
  • factory.go:定义抽象工厂接口和具体工厂。

    创建一个 FurnitureFactory 接口,包含两个方法:

    • CreateChair() Chair
    • CreateTable() Table

    实现两个具体工厂:

    • ModernFactory,生产 ModernChair 和 ModernTable
    • VintageFactory,生产 VintageChair 和 VintageTable

    另外,创建函数 GetFactory(style string) FurnitureFactory,根据风格(modern 或 vintage)返回相应的工厂。对于无法识别的风格,默认使用 ModernFactory。

  • main.go:使用工厂为房间配备家具。

    创建函数 FurnishRoom(factory FurnitureFactory) string,使用工厂创建椅子和桌子,然后返回一个格式化字符串,在两行中分别显示这两项操作。

    读取风格输入,获取相应的工厂,为房间配备家具,并打印结果。

输入如下:

  • 第 1 行:家具风格(modern、vintage 或其他内容)

例如,输入:

modern

输出应为:

Sitting on a sleek modern chair
Placing items on a glass modern table

输入:

vintage

输出应为:

Sitting on an ornate vintage chair
Placing items on a wooden vintage table

输入:

rustic

输出应为:

Sitting on a sleek modern chair
Placing items on a glass modern table

注意,FurnishRoom 函数通过接口与任何工厂配合使用。它并不知道自己创建的是现代家具还是复古家具。这样可以确保房间里的所有家具都来自同一个系列,保持风格一致!

亲手试试

package main

import "fmt"

// FurnishRoom 使用工厂创建家具并返回结果
// TODO: 实现这个函数
// - 使用工厂创建一把椅子和一张桌子
// - 返回一个字符串,第一行是 SitOn() 的结果,第二行是 PlaceItems() 的结果
func FurnishRoom(factory FurnitureFactory) string {
	// TODO: 使用工厂创建椅子和桌子
	// TODO: 返回格式化的字符串,将两个操作的结果分别放在不同行
	return ""
}

func main() {
	var style string
	fmt.Scanln(&style)

	// TODO: 根据 style 获取合适的工厂
	// TODO: 使用工厂布置房间
	// TODO: 打印结果
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Go 在线编译器