【创造型模式】工厂方法模式

文章目录

工厂方法模式

今天我们继续学习一例创造型设计模式------工厂方法模式。参考的主要资料是刘丹冰老师的《Easy 搞定 Golang 设计模式》

工厂方法模式当中的角色和职责

简单来说,工厂方法模式 = 简单工厂模式 + "开闭原则"。工厂方法模式当中具有以下几个角色:

  • 抽象工厂角色:工厂方法模式的核心,任何工厂类都必须实现这个接口。「与"简单工厂模式"相比,工厂方法模式进一步对"工厂"这个角色进行了抽象,而在"简单工厂模式"当中仅使用一个工厂来负责所有结构实例的创建」
  • 具体工厂角色:即具体的工厂,它是抽象工厂的实现,负责是厉害产品对象。
  • 抽象产品角色:工厂方法模式所创建的所有对象的父类,它负责描述所有实例共有的公共接口。
  • 具体产品角色:工厂方法模式所创建的具体实例。

工厂方法模式的实现

基于上述工厂方法模式的角色和职责,现在我们使用 Go 来实现一个工厂方法模式的 Demo。下例是对昨天学习的"简单工厂模式"的升级,新建了一个抽象工厂,并为每一个水果实现了具体的水果工厂,这样就可以做到"开闭原则",即"针对每一种新的水果,我不再需要修改工厂的代码,而是直接新写一个具体工厂并生产相应的水果即可"。

go 复制代码
package main

import "fmt"

// ---------- 抽象层 ----------
// 水果类(抽象接口)
type Fruit interface {
	Show()
}

// 工厂类(抽象接口)
type AbstractFactory interface {
	CreateFruit() Fruit // 后续的具体工厂需要实现 CreateFruit 方法
}

// ---------- 基础类模块 ----------
type Apple struct {
	Fruit
}

func (apple *Apple) Show() {
	fmt.Println("I'm Apple")
}

type Banana struct {
	Fruit
}

func (banana *Banana) Show() {
	fmt.Println("I'm Banana")
}

type Pear struct {
	Fruit
}

func (pear *Pear) Show() {
	fmt.Println("I'm Pear")
}

// ---------- 工厂模块 ----------
// 具体苹果工厂
type AppleFactory struct {
	AbstractFactory
}

func (af *AppleFactory) CreateFruit() Fruit {
	return &Apple{}
}

type BananaFactory struct {
	AbstractFactory
}

func (bf *BananaFactory) CreateFruit() Fruit {
	return &Banana{}
}

type PearFactory struct {
	AbstractFactory
}

func (pf *PearFactory) CreateFruit() Fruit {
	return &Pear{}
}

func main() {
	// 新建一个具体的苹果工厂
	appleFactory := &AppleFactory{}
	// 通过具体苹果工厂生产苹果实例
	apple := appleFactory.CreateFruit()
	apple.Show() // 调用苹果方法

	// 如法炮制, 新建香蕉和梨工厂并生产水果
	bananaFactory := &BananaFactory{}
	banana := bananaFactory.CreateFruit()
	banana.Show()

	pearFactory := &PearFactory{}
	pear := pearFactory.CreateFruit()
	pear.Show()
}

需要注意的是,main 函数的实现我与刘丹冰老师给出的 Demo 实现不太一样。原文当中通过var新建了appleFac这个AbstractFactory变量,并使用new对工厂进行初始化。简而言之就是在业务逻辑层面向抽象层开发,只与工厂耦合,并且只与抽象的工厂和抽象的水果类耦合,遵循了面向抽象层接口编程的原则。一个按照上述原则重新实现的main函数如下:

go 复制代码
func main() {
	var appleFac AbstractFactory
	appleFac = &AppleFactory{}
	var apple Fruit
	apple = appleFac.CreateFruit()
	apple.Show()

	var bananaFac AbstractFactory
	bananaFac = &BananaFactory{}
	var banana Fruit
	banana = bananaFac.CreateFruit()
	banana.Show()

	var pearFac AbstractFactory
	pearFac = &PearFactory{}
	var pear Fruit
	pear = pearFac.CreateFruit()
	pear.Show()
}

为了进一步体现工厂方法模式的开闭原则,接下来我们尝试在原有代码的基础上添加一个新产品的生产,比如"山东苹果",具体添加的代码如下:

go 复制代码
/*... ... ...*/

type ShanDongApple struct {
	Fruit
}

func (sda *ShanDongApple) Show() {
	fmt.Println("I'm Apple from ShanDong")
}

/*... ... ...*/

type ShanDongAppleFactory struct {
	AbstractFactory
}

func (sdaf *ShanDongAppleFactory) CreateFruit() Fruit {
	return &ShanDongApple{}
}

/*... ... ...*/

func main() {
    /*... ... ...*/
    var shandongAppleFac AbstractFactory
	shandongAppleFac = &ShanDongAppleFactory{}
	var shandongApple Fruit
	shandongApple = shandongAppleFac.CreateFruit()
	shandongApple.Show()
}

可以看到,新增的基本类"山东苹果"和"山东苹果工厂"没有改动之前的任何代码,完全符合开闭原则思想。新增的功能不会影响到之前已有系统的稳定性。与之相比,如果采用简单工厂模式,那么针对新增的"山东苹果"产品,必须修改工厂,添加与"山东苹果"相关的生产逻辑,这样就会改动已有的业务逻辑,不够好。

工厂方法模式的优缺点

优点

  • 不需要记住具体类名,甚至连具体参数都不需要记忆;
  • 实现了对象创建和使用的分离;
  • 系统的可拓展性非常好,无需改动接口和原类;
  • 新产品的创建符合开闭原则。

缺点

  • 会增加系统中类的个数,复杂度增加,需要更长的时间理解;
  • 增加了系统的抽象性和理解难度。

适用场景

  • 客户端不知道它需要的对象的类。
  • 抽象工厂类通过其子类来指定创建哪个对象。
相关推荐
Zane19941 天前
一个 new 就能创建对象,为什么还要拆出单例、工厂、建造者、原型四种模式?
设计模式
怕浪猫2 天前
一行命令复刻爆款视频,我把 Hypit 从安装跑到了出片
人工智能·设计模式·程序员
Zane19942 天前
函数式编程里的函数,其实不是你天天写的那个函数——三大编程范式的边界在哪
设计模式
Zane19943 天前
策略模式现在该不该上?一次讲清楚过度设计和设计不足怎么找平衡
设计模式
她说..3 天前
常见设计模式-模板方法模式
java·spring·设计模式·springboot
xiaofeiyang1503 天前
第六章 · 桥接 — 三支毛笔,画出九种颜色
设计模式
Shadow(⊙o⊙)4 天前
OTOL设计模式 One Thread One Loop
服务器·网络·设计模式
sarasuki5 天前
如何让LLM 能在半夜偷偷打开网易云呢?
人工智能·设计模式·agent
小王师傅665 天前
【设计模式】装饰模式(四):框架源码实战——从 Java I/O 到 Spring 到 MyBatis
java·设计模式
执明wa5 天前
Android RecyclerView 多类型, 多种 Item
android·xml·开发语言·设计模式·android studio