GO语言实现设计模式全

Posted 恋喵大鲤鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GO语言实现设计模式全相关的知识,希望对你有一定的参考价值。

文章目录

0.前言

设计模式是针对软件设计中常见问题的工具箱,其中的工具就是各种经过实践验证的解决方案。即使你从未遇到过这些问题,了解模式仍然非常有用,因为它能指导你如何使用面向对象的设计原则来解决各种问题,提高开发效率,降低开发成本;本文囊括了GO语言实现的全部23种经典设计模式示例,每个示例都精心设计,力求符合模式结构,可作为日常编码参考,同时一些常用的设计模式融入了开发实践经验总结,帮助大家在平时工作中灵活运用。

本文共收录了 23 种设计模式,这些设计模式被分为三大类:创建型、结构型和行为型,分别包括内容如下:

创建型模式5个:
单例模式(Singleton)、抽象工厂模式(Abstract Factory)、建造者模式(Builder)、工厂模式(Factory)、原型模式(Prototype)。

结构型模式7个:
适配器模式(Adapter)、桥接模式(Bridge)、装饰模式(Decorator)、组合模式(Composite)、外观模式(Facade)、享元模式(Flyweight)、代理模式(Proxy)。

行为型模式11个:
模版方法模式(Template Method)、命令模式(Command)、迭代器模式(Iterator)、观察者模式(Observer)、中介者模式(Mediator)、备忘录模式(Memento)、解释器模式(Interpreter)、状态模式(State)、策略模式(Strategy)、责任链模式(Chain of Responsibility)、访问者模式(Visitor)。

事实上,除了 GoF 的 23 个著名设计模式之外,还有很多别的设计模式,Wrapper 模式、DAO(Data Access Object)模式、MVC(Model-View-Control)模式等。设计模式的学习是一个艰苦漫长的过程,需要大量的实践、思考和总结,即便如此,我们首先要有这个学习的意识。

1.责任链模式

概念

责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。

该模式允许多个对象来对请求进行处理,而无需让发送者类与具体接收者类相耦合。链可在运行时由遵循标准处理者接口的任意处理者动态生成。

一般意义上的责任链模式是说,请求在链上流转时任何一个满足条件的节点处理完请求后就会停止流转并返回,不过还可以根据不同的业务情况做一些改进:

请求可以流经处理链的所有节点,不同节点会对请求做不同职责的处理;
可以通过上下文参数保存请求对象及上游节点的处理结果,供下游节点依赖,并进一步处理;
处理链可支持节点的异步处理,通过实现特定接口判断,是否需要异步处理;
责任链对于请求处理节点可以设置停止标志位,不是异常,是一种满足业务流转的中断;
责任链的拼接方式存在两种,一种是节点遍历,一个节点一个节点顺序执行;另一种是节点嵌套,内层节点嵌入在外层节点执行逻辑中,类似递归,或者“回”行结构;
责任链的节点嵌套拼接方式多被称为拦截器链或者过滤器链,更易于实现业务流程的切面,比如监控业务执行时长,日志输出,权限校验等;

示例

本示例模拟实现机场登记过程,第一步办理登机牌,第二步如果有行李,就办理托运,第三步核实身份,第四步安全检查,第五步完成登机;其中行李托运是可选的,其他步骤必选,必选步骤有任何不满足就终止登机;旅客对象作为请求参数上下文,每个步骤会根据旅客对象状态判断是否处理或流转下一个节点;

登机过程

package chainofresponsibility

import "fmt"

// BoardingProcessor 登机过程中,各节点统一处理接口
type BoardingProcessor interface 
	SetNextProcessor(processor BoardingProcessor)
	ProcessFor(passenger *Passenger)


// Passenger 旅客
type Passenger struct 
	name                  string // 姓名
	hasBoardingPass       bool   // 是否办理登机牌
	hasLuggage            bool   // 是否有行李需要托运
	isPassIdentityCheck   bool   // 是否通过身份校验
	isPassSecurityCheck   bool   // 是否通过安检
	isCompleteForBoarding bool   // 是否完成登机


// baseBoardingProcessor 登机流程处理器基类
type baseBoardingProcessor struct 
	// nextProcessor 下一个登机处理流程
	nextProcessor BoardingProcessor


// SetNextProcessor 基类中统一实现设置下一个处理器方法
func (b *baseBoardingProcessor) SetNextProcessor(processor BoardingProcessor) 
	b.nextProcessor = processor


// ProcessFor 基类中统一实现下一个处理器流转
func (b *baseBoardingProcessor) ProcessFor(passenger *Passenger) 
	if b.nextProcessor != nil 
		b.nextProcessor.ProcessFor(passenger)
	


// boardingPassProcessor 办理登机牌处理器
type boardingPassProcessor struct 
	baseBoardingProcessor // 引用基类


func (b *boardingPassProcessor) ProcessFor(passenger *Passenger) 
	if !passenger.hasBoardingPass 
		fmt.Printf("为旅客%s办理登机牌;\\n", passenger.name)
		passenger.hasBoardingPass = true
	
	// 成功办理登机牌后,进入下一个流程处理
	b.baseBoardingProcessor.ProcessFor(passenger)


// luggageCheckInProcessor 托运行李处理器
type luggageCheckInProcessor struct 
	baseBoardingProcessor


func (l *luggageCheckInProcessor) ProcessFor(passenger *Passenger) 
	if !passenger.hasBoardingPass 
		fmt.Printf("旅客%s未办理登机牌,不能托运行李;\\n", passenger.name)
		return
	
	if passenger.hasLuggage 
		fmt.Printf("为旅客%s办理行李托运;\\n", passenger.name)
	
	l.baseBoardingProcessor.ProcessFor(passenger)


// identityCheckProcessor 校验身份处理器
type identityCheckProcessor struct 
	baseBoardingProcessor


func (i *identityCheckProcessor) ProcessFor(passenger *Passenger) 
	if !passenger.hasBoardingPass 
		fmt.Printf("旅客%s未办理登机牌,不能办理身份校验;\\n", passenger.name)
		return
	
	if !passenger.isPassIdentityCheck 
		fmt.Printf("为旅客%s核实身份信息;\\n", passenger.name)
		passenger.isPassIdentityCheck = true
	
	i.baseBoardingProcessor.ProcessFor(passenger)


// securityCheckProcessor 安检处理器
type securityCheckProcessor struct 
	baseBoardingProcessor


func (s *securityCheckProcessor) ProcessFor(passenger *Passenger) 
	if !passenger.hasBoardingPass 
		fmt.Printf("旅客%s未办理登机牌,不能进行安检;\\n", passenger.name)
		return
	
	if !passenger.isPassSecurityCheck 
		fmt.Printf("为旅客%s进行安检;\\n", passenger.name)
		passenger.isPassSecurityCheck = true
	
	s.baseBoardingProcessor.ProcessFor(passenger)


// completeBoardingProcessor 完成登机处理器
type completeBoardingProcessor struct 
	baseBoardingProcessor


func (c *completeBoardingProcessor) ProcessFor(passenger *Passenger) 
	if !passenger.hasBoardingPass ||
		!passenger.isPassIdentityCheck ||
		!passenger.isPassSecurityCheck 
		fmt.Printf("旅客%s登机检查过程未完成,不能登机;\\n", passenger.name)
		return
	
	passenger.isCompleteForBoarding = true
	fmt.Printf("旅客%s成功登机;\\n", passenger.name)

测试程序

package chainofresponsibility

import "testing"

func TestChainOfResponsibility(t *testing.T) 
	boardingProcessor := BuildBoardingProcessorChain()
	passenger := &Passenger
		name:                  "李四",
		hasBoardingPass:       false,
		hasLuggage:            true,
		isPassIdentityCheck:   false,
		isPassSecurityCheck:   false,
		isCompleteForBoarding: false,
	
	boardingProcessor.ProcessFor(passenger)


// BuildBoardingProcessorChain 构建登机流程处理链
func BuildBoardingProcessorChain() BoardingProcessor 
	completeBoardingNode := &completeBoardingProcessor

	securityCheckNode := &securityCheckProcessor
	securityCheckNode.SetNextProcessor(completeBoardingNode)

	identityCheckNode := &identityCheckProcessor
	identityCheckNode.SetNextProcessor(securityCheckNode)

	luggageCheckInNode := &luggageCheckInProcessor
	luggageCheckInNode.SetNextProcessor(identityCheckNode)

	boardingPassNode := &boardingPassProcessor
	boardingPassNode.SetNextProcessor(luggageCheckInNode)
	return boardingPassNode

运行结果

=== RUN   TestChainOfResponsibility
为旅客李四办理登机牌;
为旅客李四办理行李托运;
为旅客李四核实身份信息;
为旅客李四进行安检;
旅客李四成功登机;
--- PASS: TestChainOfResponsibility (0.00s)
PASS

2.命令模式

概念

命令模式是一种行为设计模式,它可将请求转换为一个包含与请求相关的所有信息的独立对象。该转换让你能根据不同的请求将方法参数化、延迟请求执行或将其放入队列中,且能实现可撤销操作。

方法参数化是指将每个请求参数传入具体命令的工厂方法(go语言没有构造函数)创建命令,同时具体命令会默认设置好接受对象,这样做的好处是不管请求参数个数及类型,还是接受对象有几个,都会被封装到具体命令对象的成员字段上,并通过统一的Execute接口方法进行调用,屏蔽各个请求的差异,便于命令扩展,多命令组装,回滚等;

示例

控制电饭煲做饭是一个典型的命令模式的场景,电饭煲的控制面板会提供设置煮粥、蒸饭模式,及开始和停止按钮,电饭煲控制系统会根据模式的不同设置相应的火力,压强及时间等参数;煮粥,蒸饭就相当于不同的命令,开始按钮就相当命令触发器,设置好做饭模式,点击开始按钮电饭煲就开始运行,同时还支持停止命令;

电饭煲接收器

package command

import "fmt"

// ElectricCooker 电饭煲
type ElectricCooker struct 
	fire     string // 火力
	pressure string // 压力


// SetFire 设置火力
func (e *ElectricCooker) SetFire(fire string) 
	e.fire = fire


// SetPressure 设置压力
func (e *ElectricCooker) SetPressure(pressure string) 
	e.pressure = pressure


// Run 持续运行指定时间
func (e *ElectricCooker) Run(duration string) string 
	return fmt.Sprintf("电饭煲设置火力为%s,压力为%s,持续运行%s;", e.fire, e.pressure, duration)


// Shutdown 停止
func (e *ElectricCooker) Shutdown() string 
	return "电饭煲停止运行。"

电饭煲命令

package command

// CookCommand 做饭指令接口
type CookCommand interface 
	Execute() string // 指令执行方法


// steamRiceCommand 蒸饭指令
type steamRiceCommand struct 
	electricCooker *ElectricCooker // 电饭煲


func NewSteamRiceCommand(electricCooker *ElectricCooker) *steamRiceCommand 
	return &steamRiceCommand
		electricCooker: electricCooker,
	


func (s *steamRiceCommand) Execute() string 
	s.electricCooker.SetFire("中")
	s.electricCooker.SetPressure("正常")
	return "蒸饭:" + s.electricCooker.Run("30分钟")


// cookCongeeCommand 煮粥指令
type cookCongeeCommand struct 
	electricCooker *ElectricCooker


func NewCookCongeeCommand(electricCooker *ElectricCooker) *cookCongeeCommand 
	return &cookCongeeCommand
		electricCooker: electricCooker,
	


func (c *cookCongeeCommand) Execute() string 
	c.electricCooker.SetFire("大")
	c.electricCooker.SetPressure("强")
	return "煮粥:" + c.electricCooker.Run("45分钟")


// shutdownCommand 停止指令
type shutdownCommand struct 
	electricCooker *ElectricCooker


func NewShutdownCommand(electricCooker *ElectricCooker) *shutdownCommand 
	return &shutdownCommand
		electricCooker: electricCooker,
	


func (s *shutdownCommand) Execute() string 
	return s.electricCooker.Shutdown()


// ElectricCookerInvoker 电饭煲指令触发器
type ElectricCookerInvoker struct 
	cookCommand CookCommand


// SetCookCommand 设置指令
func (e *ElectricCookerInvoker) SetCookCommand(cookCommand CookCommand) 
	e.cookCommand = cookCommand


// ExecuteCookCommand 执行指令
func (e *ElectricCookerInvoker) ExecuteCookCommand() string 
	return e.cookCommand.Execute()

测试程序

package command

import (
	"fmt"
	"testing"
)

func TestCommand(t *testing.T) 
	// 创建电饭煲,命令接受者
	electricCooker := new(ElectricCooker)
	// 创建电饭煲指令触发器
	electricCookerInvoker := new(ElectricCookerInvoker)

	// 蒸饭
	steamRiceCommand := NewSteamRiceCommand(electricCooker)
	electricCookerInvoker.SetCookCommand(steamRiceCommand)
	fmt.Println(electricCookerInvoker.ExecuteCookCommand())

	// 煮粥
	cookCongeeCommand := NewCookCongeeCommand(electricCooker)
	electricCookerInvoker.SetCookCommand(cookCongeeCommand)
	fmt.Println(electricCookerInvoker.ExecuteCookCommand())

	// 停止
	shutdownCommand := NewShutdownCommand(electricCooker)
	electricCookerInvoker.SetCookCommand(shutdownCommand)
	fmt.Println(electricCookerInvoker.ExecuteCookCommand())

运行结果

=== RUN   TestCommand
蒸饭:电饭煲设置火力为中,压力为正常,持续运行30分钟;
煮粥:电饭煲设置火力为大,压力为强,持续运行45分钟;
电饭煲停止运行。
--- PASS: TestCommand (0.00s)
PASS

3.迭代器模式

概念

迭代器模式是一种行为设计模式,让你能在不暴露集合底层表现形式 (列表、 栈和树等)的情况下遍历集合中所有的元素。

在迭代器的帮助下, 客户端可以用一个迭代器接口以相似的方式遍历不同集合中的元素。

这里需要注意的是有两个典型的迭代器接口需要分清楚;一个是集合类实现的可以创建迭代器的工厂方法接口一般命名为Iterable,包含的方法类似CreateIterator;另一个是迭代器本身的接口,命名为Iterator,有Next及hasMore两个主要方法;

示例

一个班级类中包括一个老师和若干个学生,我们要对班级所有成员进行遍历,班级中老师存储在单独的结构字段中,学生存储在另外一个slice字段中,通过迭代器,我们实现统一遍历处理;

班级成员

package iterator

import "fmt"

// Member 成员接口
type Member interface 
	Desc() string // 输出成员描述信息


// Teacher 老师
type Teacher struct 
	name    string // 名称
	subject string // 所教课程


// NewTeacher 根据姓名、课程创建老师对象
func NewTeacher(name, subject string) *Teacher 
	return &Teacher
		name:    name,
		subject: subject,
	


func (t *Teacher) Desc() string 
	return fmt.Sprintf("%s班主任老师负责教%s", t.name, t.subject)


// Student 学生
type Student struct 
	name     string // 姓名
	sumScore int    // 考试总分数


// NewStudent 创建学生对象
func NewStudent(name string, sumScore int) *Student 
	return &Student
		name:     name,
		sumScore: sumScore,
	


func (t *Student) Desc() string 
	return fmt.Sprintf("%s同学考试总分为%d", t.name, t.sumScore)

班级成员迭代器
package iterator

// Iterator 迭代器接口
type Iterator interface 
	Next() Member  // 迭代下一个成员
	HasMore() bool // 是否还有


// memberIterator 班级成员迭代器实现
type memberIterator struct 
	class *Class // 需迭代的班级
	index int    // 迭代索引


func (m *memberIterator) Next() Member 
	// 迭代索引为-1时,返回老师成员,否则遍历学生slice
	if m.index == -1 
		m.index++
		return m.class.teacher
	
	student := m.class.students[m.index]
	m.index++
	return student


func (m *memberIterator) HasMore() bool 
	return m.index < len(m.class.students)


// Iterable 可迭代集合接口,实现此接口返回迭代器
type Iterable interface 
	CreateIterator() Iterator


// Class 班级,包括老师和同学
type Class struct 
	name     string
	teacher  *Teacher
	students []*Student


// NewClass 根据班主任老师名称,授课创建班级
func NewClass(name, teacherName, teacherSubject string) *Class 
	return &Class
		name:    name,
		teacher: NewTeacher(teacherName, teacherSubject),
	


// CreateIterator 创建班级迭代器
func (c *Class) CreateIterator() Iterator 
	return &memberIterator
		class: c,
		index: -1, // 迭代索引初始化为-1,从老师开始迭代
	


func (c *Class) Name() string 
	return c.name


// AddStudent 班级添加同学
func (c *Class) AddStudent(students ...*Student) 
	c.students = append(c.students, students...)

测试程序
package iterator

import (
	"fmt"
	"testing"
)

func TestIterator(t *testing.T) 
	class := NewClass("三年级一班", "王明", "数学课")
	class.AddStudent(NewStudent("张三", 389),
		NewStudent("李四", 378),
		NewStudent("王五", 347)以上是关于GO语言实现设计模式全的主要内容,如果未能解决你的问题,请参考以下文章

go·美食煎饼馃子百强评选大赛总浏览量突破450万人次!

《Head first设计模式》学习笔记 – 迭代器模式

Go语言中的Interface

Go语言中不一样的Interface

GO高阶: 调度器 GMP 原理与调度全分析

Go原生插件使用问题全解析