Go 语言实现创建型设计模式 - 工厂模式

简介: Go 语言实现创建型设计模式 - 工厂模式

介绍

工厂模式是一种创建型设计模式,包含三种类型,分别是简单工厂、工厂方法和抽象工厂。

《设计模式》[1]一书中,因为 GoF[2] 认为简单工厂是工厂方法的一种特例,所以 GoF 把工厂模式分为两种类型,分别是工厂方法和抽象工厂。

本文我们使用第一种分类方式,分别介绍一下工厂模式的三种类型。

使用场景

在介绍工厂模式的使用场景之前,我们需要先简单了解工厂模式的组成结构,一般分为抽象产品、具体产品、抽象工厂和具体工厂。

注意:简单工厂模式,不区分抽象工厂和具体工厂。

简单工厂

简单工厂适用于具体产品较少,且不会频繁添加具体产品的场景。因为每多一个具体产品,在工厂中就多一个 if 分支。

工厂方法

工厂方法适用于具体产品较多,且需要频繁添加具体产品的场景。使用工厂方法可以避免使用 if 分支,当我们需要添加具体产品时,只需创建新的具体产品和具体工厂,符合开闭原则和单一职责原则。

而且还可以将每个具体产品的创建逻辑拆分到不同的工厂中,避免使用一个工厂导致的代码过于复杂。

注意:拆分多个工厂,则需要维护多个工厂的代码。

抽象工厂

抽象工厂适用于一个具体工厂需要负责生产多个不同产品,并且工厂的职责不会继续增加的场景(即抽象工厂定义的一组方法不会继续增加)。

否则,不仅所有具体工厂都需要修改,抽象产品和具体产品也需要修改,违反开闭原则。

03

实现方式

简单工厂

简单工厂模式违反了开闭原则,严格意义上不算是一个设计模式,它主要包括三个结构,分别是工厂、抽象产品和具体产品。

  • 工厂 - 负责调用具体产品生产产品,返回值是抽象产品(接口)。
  • 抽象产品 - 负责定义产品,接口类型,包含一组方法。
  • 具体产品 - 负责被工厂调用,实现抽象产品(接口)。

工厂方法

工厂方法模式符合开闭原则,它相比简单工厂模式,多了一个抽象工厂的结构,总共包括四个结构,分别是抽象工厂、具体工厂、抽象产品和具体产品。

  • 抽象工厂(单个) - 负责定义工厂,接口类型,包含一组方法。
  • 具体工厂(多个) - 负责通过实例化具体产品创建产品,实现抽象工厂(接口)。
  • 抽象产品(单个) - 负责定义产品,接口类型,包含一组方法。
  • 具体产品(多个) - 负责被具体工厂调用,实现抽象产品(接口)。

注意:此处“抽象工厂”是工厂方法模式中的一个结构,不要与抽象工厂模式混淆。

抽象工厂

抽象工厂模式也是总共包括四个结构,它与工厂方法模式不同,工厂方法模式中抽象产品只有一个,而抽象工厂模式抽象产品有多个。

但是,四个结构的职责与工厂方法模式相同。

  • 抽象工厂(单个)
  • 具体工厂(多个)
  • 抽象产品(多个)
  • 具体产品(多个)

04

Go 实现

简单工厂

// IDrink 抽象产品 - 饮料
type IDrink interface {
 Kind() // 抽象方法 - 类别
 Name() // 抽象方法 - 名称
}
// CocaCola 具体产品 - 可口可乐
type CocaCola struct {
}
// Kind 具体方法
func (c *CocaCola) Kind() {
 fmt.Println("carbonated drinks")
}
// Name 具体方法
func (c *CocaCola) Name() {
 fmt.Println("CocaCola")
}
// Sprite 具体产品 - 雪碧
type Sprite struct {
}
// Kind 具体方法
func (s *Sprite) Kind() {
 fmt.Println("carbonated drinks")
}
// Name 具体方法
func (s *Sprite) Name() {
 fmt.Println("Sprite")
}
// SimpleFactory 工厂
type SimpleFactory struct {
}
// Produce 生产 - 返回值(抽象产品)
func (s *SimpleFactory) Produce(name string) (drink IDrink) {
 if name == "CocaCola" {
  drink = new(CocaCola)
 } else if name == "Sprite" {
  drink = new(Sprite)
 }
 return
}

阅读上面这段代码,我们可以发现,我们通过代码定义简单工厂模式的三个结构。

定义一个包含一组方法的 IDrink 接口,代表抽象产品;

定义一个 CocaCola 结构体和一个 Sprite 结构体,并都实现 IDrink 接口,代表具体产品;

定义一个 SimpleFactory 结构体,并定义一个返回值是 IDrinkProduce 方法,代表工厂。

工厂方法

// IDrink 抽象产品
type IDrink interface {
 Kind() // 抽象方法
 Name() // 抽象方法
}
// CocaCola 具体产品
type CocaCola struct {
}
// Kind 具体方法
func (c *CocaCola) Kind() {
 fmt.Println("carbonated drinks")
}
// Name 具体方法
func (c *CocaCola) Name() {
 fmt.Println("CocaCola")
}
// Sprite 具体产品
type Sprite struct {
}
// Kind 具体方法
func (s *Sprite) Kind() {
 fmt.Println("carbonated drinks")
}
// Name 具体方法
func (s *Sprite) Name() {
 fmt.Println("Sprite")
}
// IFactory 抽象工厂
type IFactory interface {
 Produce() IDrink // 抽象方法
}
// CocaColaFactory 具体工厂
type CocaColaFactory struct {
}
// Produce 具体方法
func (c *CocaColaFactory) Produce() (drink IDrink) {
 drink = new(CocaCola)
 return
}
// SpriteFactory 具体工厂
type SpriteFactory struct {
}
// Produce 具体方法
func (s *SpriteFactory) Produce() (drink IDrink) {
 drink = new(Sprite)
 return
}

阅读上面这段代码,我们通过代码定义工厂方法模式的四个结构。

定义一个包含一组方法的 IDrink 接口,代表抽象产品;

定义一个 CocaCola 结构体和一个 Sprite 结构体,并都实现 IDrink 接口,代表具体产品;

定义一个包含一组方法的 IFactory 接口,代表抽象工厂;

定义一个 CocaColaFactory 结构体和一个 SpriteFactory 结构体,并都实现 IFactory 接口,代表具体工厂;

抽象工厂

// AbstractCola 抽象 Cola
type AbstractCola interface {
 ColaKind() // 抽象方法
 ColaName() // 抽象方法
}
// AbstractSprite 抽象 Sprite
type AbstractSprite interface {
 SpriteKind() // 抽象方法
 SpriteName() // 抽象方法
}
// AbstractFactory 抽象工厂
type AbstractFactory interface {
 ProduceCola() AbstractCola // 抽象方法
 ProduceSprite() AbstractSprite // 抽象方法
}
// CocaBrandCola 可口品牌 具体 Cola 产品
type CocaBrandCola struct {
}
func (c *CocaBrandCola) ColaKind() {
 fmt.Println("Coca Brand carbonated drinks")
}
func (c *CocaBrandCola) ColaName() {
 fmt.Println("Coca Brand Cola")
}
// CocaBrandSprite 可口品牌 具体 Sprite 产品
type CocaBrandSprite struct {
}
func (c *CocaBrandSprite) SpriteKind() {
 fmt.Println("Coca Brand carbonated drinks")
}
func (c *CocaBrandSprite) SpriteName() {
 fmt.Println("Coca Brand Sprite")
}
// CocaFactory 可口品牌 具体工厂
type CocaFactory struct {
}
func (c *CocaFactory) ProduceCola() (cola AbstractCola) {
 cola = new(CocaBrandCola)
 return
}
func (c *CocaFactory) ProduceSprite() (sprite AbstractSprite) {
 sprite = new(CocaBrandSprite)
 return
}
// PepsiBrandCola 百事品牌 具体 Cola 产品
type PepsiBrandCola struct {
}
func (p *PepsiBrandCola) ColaKind() {
 fmt.Println("Pepsi Brand carbonated drinks")
}
func (p *PepsiBrandCola) ColaName() {
 fmt.Println("Pepsi Brand Cola")
}
// PepsiBrandSprite 百事品牌 具体 Sprite 产品
type PepsiBrandSprite struct {
}
func (p *PepsiBrandSprite) SpriteKind() {
 fmt.Println("Pepsi Brand carbonated drinks")
}
func (p *PepsiBrandSprite) SpriteName() {
 fmt.Println("Pepsi Brand Sprite")
}
// PepsiFactory 百事品牌 具体工厂
type PepsiFactory struct {
}
func (p *PepsiFactory) ProduceCola() (cola AbstractCola) {
 cola = new(PepsiBrandCola)
 return
}
func (p *PepsiFactory) ProduceSprite() (sprite AbstractSprite) {
 sprite = new(PepsiBrandSprite)
 return
}

阅读上面这段代码,我们通过代码定义抽象工厂模式的四个结构。

定义一个包含一组方法的 AbstractCola 接口,和一个包含一组方法的 AbstractSprite 接口,均代表抽象产品(多个抽象产品);

定义一个 CocaBrandCola 结构体,实现 AbstractCola 接口;定义一个 CocaBrandSprite 结构体,实现 AbstractSprite 接口;均代表具体产品(多个具体产品);

定义一个包含一组方法的 AbstractFactory 接口,代表抽象工厂;

定义一个 CocaFactory 结构体,实现 AbstractFactory 接口;定义一个 PepsiFactory 结构体,实现 AbstractFactory 接口;均代表具体工厂(多个具体工厂);

05

总结

本文介绍的三种工厂模式中,简单工厂和工厂方法比较常用,抽象工厂使用较少。

其中,简单工厂适用于具体产品较少,且不会频繁添加具体产品的场景;

工厂方法适用于具体产品较多,且需要频繁添加具体产品的场景;

还有就是生产具体产品,代码比较复杂,不只是实例化具体产品,还需要其他业务逻辑的场景;

或不希望代码中使用一堆 if 分支的场景。

目录
相关文章
|
8天前
|
监控 算法 Go
Golang深入浅出之-Go语言中的服务熔断、降级与限流策略
【5月更文挑战第4天】本文探讨了分布式系统中保障稳定性的重要策略:服务熔断、降级和限流。服务熔断通过快速失败和暂停故障服务调用来保护系统;服务降级在压力大时提供有限功能以保持整体可用性;限流控制访问频率,防止过载。文中列举了常见问题、解决方案,并提供了Go语言实现示例。合理应用这些策略能增强系统韧性和可用性。
36 0
|
1天前
|
存储 编译器 Go
Go语言学习12-数据的使用
【5月更文挑战第5天】本篇 Huazie 向大家介绍 Go 语言数据的使用,包含赋值语句、常量与变量、可比性与有序性
17 6
Go语言学习12-数据的使用
|
2天前
|
Java Go
一文带你速通go语言指针
Go语言指针入门指南:简述指针用于提升效率,通过地址操作变量。文章作者sharkChili是Java/CSDN专家,维护Java Guide项目。文中介绍指针声明、取值,展示如何通过指针修改变量值及在函数中的应用。通过实例解析如何使用指针优化函数,以实现对原变量的直接修改。作者还邀请读者加入交流群深入探讨,并鼓励关注其公众号“写代码的SharkChili”。
9 0
|
2天前
|
存储 缓存 Java
来聊聊go语言的hashMap
本文介绍了Go语言中的`map`与Java的不同设计思想。作者`sharkChili`是一名Java和Go开发者,同时也是CSDN博客专家及JavaGuide项目的维护者。文章探讨了Go语言`map`的数据结构,包括`count`、`buckets指针`和`bmap`,解释了键值对的存储方式,如何利用内存对齐优化空间使用,并展示了`map`的初始化、插入键值对以及查找数据的源码过程。此外,作者还分享了如何通过汇编查看`map`操作,并鼓励读者深入研究Go的哈希冲突解决和源码。最后,作者提供了一个交流群,供读者讨论相关话题。
10 0
|
3天前
|
Java Go
Go语言学习11-数据初始化
【5月更文挑战第3天】本篇带大家通过内建函数 new 和 make 了解Go语言的数据初始化过程
17 1
Go语言学习11-数据初始化
|
3天前
|
自然语言处理 安全 Java
速通Go语言编译过程
Go语言编译过程详解:从词法分析(生成token)到句法分析(构建语法树),再到语义分析(类型检查、推断、匹配及函数内联)、生成中间码(SSA)和汇编码。最后,通过链接生成可执行文件。作者sharkchili,CSDN Java博客专家,分享技术细节,邀请读者加入交流群。
22 2
|
4天前
|
Java Linux Go
一文带你速通Go语言基础语法
本文是关于Go语言的入门介绍,作者因其简洁高效的特性对Go语言情有独钟。文章首先概述了Go语言的优势,包括快速上手、并发编程简单、设计简洁且功能强大,以及丰富的标准库。接着,文章通过示例展示了如何编写和运行Go代码,包括声明包、导入包和输出语句。此外,还介绍了Go的语法基础,如变量类型(数字、字符串、布尔和复数)、变量赋值、类型转换和默认值。文章还涉及条件分支(if和switch)和循环结构(for)。最后,简要提到了Go函数的定义和多返回值特性,以及一些常见的Go命令。作者计划在后续文章中进一步探讨Go语言的其他方面。
10 0
|
5天前
|
JavaScript 前端开发 Go
Go语言的入门学习
【4月更文挑战第7天】Go语言,通常称为Golang,是由Google设计并开发的一种编程语言,它于2009年公开发布。Go的设计团队主要包括Robert Griesemer、Rob Pike和Ken Thompson,这三位都是计算机科学和软件工程领域的杰出人物。
13 1
|
5天前
|
Go
|
6天前
|
分布式计算 Java Go
Golang深入浅出之-Go语言中的分布式计算框架Apache Beam
【5月更文挑战第6天】Apache Beam是一个统一的编程模型,适用于批处理和流处理,主要支持Java和Python,但也提供实验性的Go SDK。Go SDK的基本概念包括`PTransform`、`PCollection`和`Pipeline`。在使用中,需注意类型转换、窗口和触发器配置、资源管理和错误处理。尽管Go SDK文档有限,生态系统尚不成熟,且性能可能不高,但它仍为分布式计算提供了可移植的解决方案。通过理解和掌握Beam模型,开发者能编写高效的数据处理程序。
134 1