建设部网站法律法规,天津市做公司网站的公司,广州越秀区核酸检测点,网站开发模块分类导语| 设计模式是针对软件设计中常见问题的工具箱#xff0c;其中的工具就是各种经过实践验证的解决方案。即使你从未遇到过这些问题#xff0c;了解模式仍然非常有用#xff0c;因为它能指导你如何使用面向对象的设计原则来解决各种问题#xff0c;提高开发效率#xff0…导语| 设计模式是针对软件设计中常见问题的工具箱其中的工具就是各种经过实践验证的解决方案。即使你从未遇到过这些问题了解模式仍然非常有用因为它能指导你如何使用面向对象的设计原则来解决各种问题提高开发效率降低开发成本本文囊括了GO语言实现的经典设计模式示例每个示例都精心设计力求符合模式结构可作为日常编码参考同时一些常用的设计模式融入了开发实践经验总结帮助大家在平时工作中灵活运用。 责任链模式
(一概念
责任链模式是一种行为设计模式 允许你将请求沿着处理者链进行发送。收到请求后每个处理者均可对请求进行处理或将其传递给链上的下个处理者。
该模式允许多个对象来对请求进行处理而无需让发送者类与具体接收者类相耦合。链可在运行时由遵循标准处理者接口的任意处理者动态生成。 一般意义上的责任链模式是说请求在链上流转时任何一个满足条件的节点处理完请求后就会停止流转并返回不过还可以根据不同的业务情况做一些改进
请求可以流经处理链的所有节点不同节点会对请求做不同职责的处理可以通过上下文参数保存请求对象及上游节点的处理结果供下游节点依赖并进一步处理处理链可支持节点的异步处理通过实现特定接口判断是否需要异步处理责任链对于请求处理节点可以设置停止标志位不是异常是一种满足业务流转的中断责任链的拼接方式存在两种一种是节点遍历一个节点一个节点顺序执行另一种是节点嵌套内层节点嵌入在外层节点执行逻辑中类似递归或者“回”行结构责任链的节点嵌套拼接方式多被称为拦截器链或者过滤器链更易于实现业务流程的切面比如监控业务执行时长日志输出权限校验等
二示例
本示例模拟实现机场登机过程第一步办理登机牌第二步如果有行李就办理托运第三步核实身份第四步安全检查第五步完成登机其中行李托运是可选的其他步骤必选必选步骤有任何不满足就终止登机旅客对象作为请求参数上下文每个步骤会根据旅客对象状态判断是否处理或流转下一个节点
三登机过程 package chainofresponsibilityimport 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 truefmt.Printf(旅客%s成功登机;\n, passenger.name)
}
四测试程序 package chainofresponsibilityimport testingfunc 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 命令模式
一概念
命令模式是一种行为设计模式它可将请求转换为一个包含与请求相关的所有信息的独立对象。该转换让你能根据不同的请求将方法参数化、延迟请求执行或将其放入队列中且能实现可撤销操作。
方法参数化是指将每个请求参数传入具体命令的工厂方法go语言没有构造函数创建命令同时具体命令会默认设置好接受对象这样做的好处是不管请求参数个数及类型还是接受对象有几个都会被封装到具体命令对象的成员字段上并通过统一的Execute接口方法进行调用屏蔽各个请求的差异便于命令扩展多命令组装回滚等
二示例
控制电饭煲做饭是一个典型的命令模式的场景电饭煲的控制面板会提供设置煮粥、蒸饭模式及开始和停止按钮电饭煲控制系统会根据模式的不同设置相应的火力压强及时间等参数煮粥蒸饭就相当于不同的命令开始按钮就相当命令触发器设置好做饭模式点击开始按钮电饭煲就开始运行同时还支持停止命令
三电饭煲接收器 package commandimport 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 commandimport (fmttesting
)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 迭代器模式
一概念 迭代器模式是一种行为设计模式让你能在不暴露集合底层表现形式 列表、 栈和树等的情况下遍历集合中所有的元素。
在迭代器的帮助下 客户端可以用一个迭代器接口以相似的方式遍历不同集合中的元素。 这里需要注意的是有两个典型的迭代器接口需要分清楚一个是集合类实现的可以创建迭代器的工厂方法接口一般命名为Iterable包含的方法类似CreateIterator另一个是迭代器本身的接口命名为Iterator有Next及hasMore两个主要方法
二示例 一个班级类中包括一个老师和若干个学生我们要对班级所有成员进行遍历班级中老师存储在单独的结构字段中学生存储在另外一个slice字段中通过迭代器我们实现统一遍历处理
三班级成员 package iteratorimport 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时返回老师成员否则遍历学生sliceif m.index -1 {m.indexreturn m.class.teacher}student : m.class.students[m.index]m.indexreturn student
}func (m *memberIterator) HasMore() bool {return m.index len(m.class.students)
}// Iterable 可迭代集合接口实现此接口返回迭代器
type Iterable interface {CreateIterator() Iterator
}// Class 班级包括老师和同学
type Class struct {name stringteacher *Teacherstudents []*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 iteratorimport (fmttesting
)func TestIterator(t *testing.T) {class : NewClass(三年级一班, 王明, 数学课)class.AddStudent(NewStudent(张三, 389),NewStudent(李四, 378),NewStudent(王五, 347))fmt.Printf(%s成员如下:\n, class.Name())classIterator : class.CreateIterator()for classIterator.HasMore() {member : classIterator.Next()fmt.Println(member.Desc())}
}
六运行结果 RUN TestIterator
三年级一班成员如下:
王明班主任老师负责教数学课
张三同学考试总分为389
李四同学考试总分为378
王五同学考试总分为347
--- PASS: TestIterator (0.00s)
PASS 中介者模式
一概念
中介者模式是一种行为设计模式能让你减少对象之间混乱无序的依赖关系。该模式会限制对象之间的直接交互迫使它们通过一个中介者对象进行合作将网状依赖变为星状依赖。
中介者能使得程序更易于修改和扩展而且能更方便地对独立的组件进行复用因为它们不再依赖于很多其他的类。 中介者模式与观察者模式之间的区别是中介者模式解决的是同类或者不同类的多个对象之间多对多的依赖关系观察者模式解决的是多个对象与一个对象之间的多对一的依赖关系。
二示例
机场塔台调度系统是一个体现中介者模式的典型示例假设是一个小机场每次只能同时允许一架飞机起降每架靠近机场的飞机需要先与塔台沟通是否可以降落如果没有空闲的跑道需要在天空盘旋等待如果有飞机离港等待的飞机会收到塔台的通知按先后顺序降落这种方式免去多架飞机同时到达机场需要相互沟通降落顺序的复杂性减少多个飞机间的依赖关系简化业务逻辑从而降低系统出问题的风险。
三飞机对象 package mediatorimport fmt// Aircraft 飞机接口
type Aircraft interface {ApproachAirport() // 抵达机场空域DepartAirport() // 飞离机场
}// airliner 客机
type airliner struct {name string // 客机型号airportMediator AirportMediator // 机场调度
}// NewAirliner 根据指定型号及机场调度创建客机
func NewAirliner(name string, mediator AirportMediator) *airliner {return airliner{name: name,airportMediator: mediator,}
}func (a *airliner) ApproachAirport() {if !a.airportMediator.CanLandAirport(a) { // 请求塔台是否可以降落fmt.Printf(机场繁忙客机%s继续等待降落;\n, a.name)return}fmt.Printf(客机%s成功滑翔降落机场;\n, a.name)
}func (a *airliner) DepartAirport() {fmt.Printf(客机%s成功滑翔起飞离开机场;\n, a.name)a.airportMediator.NotifyWaitingAircraft() // 通知等待的其他飞机
}// helicopter 直升机
type helicopter struct {name stringairportMediator AirportMediator
}// NewHelicopter 根据指定型号及机场调度创建直升机
func NewHelicopter(name string, mediator AirportMediator) *helicopter {return helicopter{name: name,airportMediator: mediator,}
}func (h *helicopter) ApproachAirport() {if !h.airportMediator.CanLandAirport(h) { // 请求塔台是否可以降落fmt.Printf(机场繁忙直升机%s继续等待降落;\n, h.name)return}fmt.Printf(直升机%s成功垂直降落机场;\n, h.name)
}func (h *helicopter) DepartAirport() {fmt.Printf(直升机%s成功垂直起飞离开机场;\n, h.name)h.airportMediator.NotifyWaitingAircraft() // 通知其他等待降落的飞机
}
四机场塔台 package mediator// AirportMediator 机场调度中介者
type AirportMediator interface {CanLandAirport(aircraft Aircraft) bool // 确认是否可以降落NotifyWaitingAircraft() // 通知等待降落的其他飞机
}// ApproachTower 机场塔台
type ApproachTower struct {hasFreeAirstrip boolwaitingQueue []Aircraft // 等待降落的飞机队列
}func (a *ApproachTower) CanLandAirport(aircraft Aircraft) bool {if a.hasFreeAirstrip {a.hasFreeAirstrip falsereturn true}// 没有空余的跑道加入等待队列a.waitingQueue append(a.waitingQueue, aircraft)return false
}func (a *ApproachTower) NotifyWaitingAircraft() {if !a.hasFreeAirstrip {a.hasFreeAirstrip true}if len(a.waitingQueue) 0 {// 如果存在等待降落的飞机通知第一个降落first : a.waitingQueue[0]a.waitingQueue a.waitingQueue[1:]first.ApproachAirport()}
}
五测试程序 package mediatorimport testingfunc TestMediator(t *testing.T) {// 创建机场调度塔台airportMediator : ApproachTower{hasFreeAirstrip: true}// 创建C919客机c919Airliner : NewAirliner(C919, airportMediator)// 创建米-26重型运输直升机m26Helicopter : NewHelicopter(米-26, airportMediator)c919Airliner.ApproachAirport() // c919进港降落m26Helicopter.ApproachAirport() // 米-26进港等待c919Airliner.DepartAirport() // c919飞离等待的米-26进港降落m26Helicopter.DepartAirport() // 最后米-26飞离
}
六运行结果 RUN TestMediator
客机C919成功滑翔降落机场;
机场繁忙直升机米-26继续等待降落;
客机C919成功滑翔起飞离开机场;
直升机米-26成功垂直降落机场;
直升机米-26成功垂直起飞离开机场;
--- PASS: TestMediator (0.00s)
PASS 备忘录模式
一概念 备忘录模式是一种行为设计模式 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。
备忘录不会影响它所处理的对象的内部结构 也不会影响快照中保存的数据。
一般情况由原发对象保存生成的备忘录对象的状态不能被除原发对象之外的对象访问所以通过内部类定义具体的备忘录对象是比较安全的但是go语言不支持内部类定义的方式因此go语言实现备忘录对象时首先将备忘录保存的状态设为非导出字段避免外部对象访问其次将原发对象的引用保存到备忘录对象中当通过备忘录对象恢复时直接操作备忘录的恢复方法将备份数据状态设置到原发对象中完成恢复。
二示例
大家平时玩的角色扮演闯关游戏的存档机制就可以通过备忘录模式实现每到一个关键关卡玩家经常会先保存游戏存档用于闯关失败后重置存档会把角色状态及场景状态保存到备忘录中同时将需要恢复游戏的引用存入备忘录用于关卡重置
三闯关游戏 package mementoimport fmt// Originator 备忘录模式原发器接口
type Originator interface {Save(tag string) Memento // 当前状态保存备忘录
}// RolesPlayGame 支持存档的RPG游戏
type RolesPlayGame struct {name string // 游戏名称rolesState []string // 游戏角色状态scenarioState string // 游戏场景状态
}// NewRolesPlayGame 根据游戏名称和角色名创建RPG游戏
func NewRolesPlayGame(name string, roleName string) *RolesPlayGame {return RolesPlayGame{name: name,rolesState: []string{roleName, 血量100}, // 默认满血scenarioState: 开始通过第一关, // 默认第一关开始}
}// Save 保存RPG游戏角色状态及场景状态到指定标签归档
func (r *RolesPlayGame) Save(tag string) Memento {return newRPGArchive(tag, r.rolesState, r.scenarioState, r)
}func (r *RolesPlayGame) SetRolesState(rolesState []string) {r.rolesState rolesState
}func (r *RolesPlayGame) SetScenarioState(scenarioState string) {r.scenarioState scenarioState
}// String 输出RPG游戏简要信息
func (r *RolesPlayGame) String() string {return fmt.Sprintf(在%s游戏中玩家使用%s,%s,%s;, r.name, r.rolesState[0], r.rolesState[1], r.scenarioState)
}
四游戏存档 package mementoimport fmt// Memento 备忘录接口
type Memento interface {Tag() string // 备忘录标签Restore() // 根据备忘录存储数据状态恢复原对象
}// rpgArchive rpg游戏存档
type rpgArchive struct {tag string // 存档标签rolesState []string // 存档的角色状态scenarioState string // 存档游戏场景状态rpg *RolesPlayGame // rpg游戏引用
}// newRPGArchive 根据标签角色状态场景状态rpg游戏引用创建游戏归档备忘录
func newRPGArchive(tag string, rolesState []string, scenarioState string, rpg *RolesPlayGame) *rpgArchive {return rpgArchive{tag: tag,rolesState: rolesState,scenarioState: scenarioState,rpg: rpg,}
}func (r *rpgArchive) Tag() string {return r.tag
}// Restore 根据归档数据恢复游戏状态
func (r *rpgArchive) Restore() {r.rpg.SetRolesState(r.rolesState)r.rpg.SetScenarioState(r.scenarioState)
}// RPGArchiveManager RPG游戏归档管理器
type RPGArchiveManager struct {archives map[string]Memento // 存储归档标签对应归档
}func NewRPGArchiveManager() *RPGArchiveManager {return RPGArchiveManager{archives: make(map[string]Memento),}
}// Reload 根据标签重新加载归档数据
func (r *RPGArchiveManager) Reload(tag string) {if archive, ok : r.archives[tag]; ok {fmt.Printf(重新加载%s;\n, tag)archive.Restore()}
}// Put 保存归档数据
func (r *RPGArchiveManager) Put(memento Memento) {r.archives[memento.Tag()] memento
}
五测试程序 package mementoimport (fmttesting
)func TestMemento(t *testing.T) {// 创建RPG游戏存档管理器rpgManager : NewRPGArchiveManager()// 创建RPG游戏rpg : NewRolesPlayGame(暗黑破坏神2, 野蛮人战士)fmt.Println(rpg) // 输出游戏当前状态rpgManager.Put(rpg.Save(第一关存档)) // 游戏存档// 第一关闯关失败rpg.SetRolesState([]string{野蛮人战士, 死亡})rpg.SetScenarioState(第一关闯关失败)fmt.Println(rpg)// 恢复存档重新闯关rpgManager.Reload(第一关存档)fmt.Println(rpg)
}
六运行结果 RUN TestMemento
在暗黑破坏神2游戏中玩家使用野蛮人战士,血量100,开始通过第一关;
在暗黑破坏神2游戏中玩家使用野蛮人战士,死亡,第一关闯关失败;
重新加载第一关存档;
在暗黑破坏神2游戏中玩家使用野蛮人战士,血量100,开始通过第一关;
--- PASS: TestMemento (0.00s)
PASS 观察者模式
一概念
观察者模式是一种行为设计模式允许你定义一种订阅机制可在对象事件发生时通知多个 “观察” 该对象的其他对象。
观察者模式提供了一种作用于任何实现了订阅者接口的对象的机制可对其事件进行订阅和取消订阅。 观察者模式是最常用的模式之一是事件总线分布式消息中间件等各种事件机制的原始理论基础常用于解耦多对一的对象依赖关系
增强的实现功能包括
当被观察者通过异步实现通知多个观察者时就相当于单进程实例的消息总线 同时还可以根据业务需要将被观察者所有数据状态变更进行分类为不同的主题观察者通过不同主题进行订阅同一个主题又可分为增加删除修改事件行为 每个主题可以实现一个线程池多个主题通过不同的线程池进行处理隔离线程池可以设置并发线程大小、缓冲区大小及调度策略比如先进先出优先级等策略观察者处理事件时有可能出现异常所以也可以注册异常处理函数异常处理也可以通过异常类型进行分类根据业务需求也可以实现通知异常重试延迟通知等功能
二示例
信用卡业务消息提醒可通过观察者模式实现业务消息包括日常消费出账单账单逾期消息提醒包括短信、邮件及电话根据不同业务的场景会采用不同的消息提醒方式或者多种消息提醒方式这里信用卡相当于被观察者观察者相当于不同的通知方式日常消费通过短信通知出账单通过邮件通知账单逾期三种方式都会进行通知
三通知方式 package observerimport fmt// Subscriber 订阅者接口
type Subscriber interface {Name() string //订阅者名称Update(message string) //订阅更新方法
}// shortMessage 信用卡消息短信订阅者
type shortMessage struct{}func (s *shortMessage) Name() string {return 手机短息
}func (s *shortMessage) Update(message string) {fmt.Printf(通过【%s】发送消息:%s\n, s.Name(), message)
}// email 信用卡消息邮箱订阅者
type email struct{}func (e *email) Name() string {return 电子邮件
}func (e *email) Update(message string) {fmt.Printf(通过【%s】发送消息:%s\n, e.Name(), message)
}// telephone 信用卡消息电话订阅者
type telephone struct{}func (t *telephone) Name() string {return 电话
}func (t *telephone) Update(message string) {fmt.Printf(通过【%s】告知:%s\n, t.Name(), message)
}
四信用卡业务 package observerimport fmt// MsgType 信用卡消息类型
type MsgType intconst (ConsumeType MsgType iota // 消费消息类型BillType // 账单消息类型ExpireType // 逾期消息类型
)// CreditCard 信用卡
type CreditCard struct {holder string // 持卡人consumeSum float32 // 消费总金额subscriberGroup map[MsgType][]Subscriber // 根据消息类型分组订阅者
}// NewCreditCard 指定持卡人创建信用卡
func NewCreditCard(holder string) *CreditCard {return CreditCard{holder: holder,subscriberGroup: make(map[MsgType][]Subscriber),}
}// Subscribe 支持订阅多种消息类型
func (c *CreditCard) Subscribe(subscriber Subscriber, msgTypes ...MsgType) {for _, msgType : range msgTypes {c.subscriberGroup[msgType] append(c.subscriberGroup[msgType], subscriber)}
}// Unsubscribe 解除订阅多种消息类型
func (c *CreditCard) Unsubscribe(subscriber Subscriber, msgTypes ...MsgType) {for _, msgType : range msgTypes {if subs, ok : c.subscriberGroup[msgType]; ok {c.subscriberGroup[msgType] removeSubscriber(subs, subscriber)}}
}func removeSubscriber(subscribers []Subscriber, toRemove Subscriber) []Subscriber {length : len(subscribers)for i, subscriber : range subscribers {if toRemove.Name() subscriber.Name() {subscribers[length-1], subscribers[i] subscribers[i], subscribers[length-1]return subscribers[:length-1]}}return subscribers
}// Consume 信用卡消费
func (c *CreditCard) Consume(money float32) {c.consumeSum moneyc.notify(ConsumeType, fmt.Sprintf(尊敬的持卡人%s,您当前消费%.2f元;, c.holder, money))
}// SendBill 发送信用卡账单
func (c *CreditCard) SendBill() {c.notify(BillType, fmt.Sprintf(尊敬的持卡人%s,您本月账单已出消费总额%.2f元;, c.holder, c.consumeSum))
}// Expire 逾期通知
func (c *CreditCard) Expire() {c.notify(ExpireType, fmt.Sprintf(尊敬的持卡人%s,您本月账单已逾期请及时还款总额%.2f元;, c.holder, c.consumeSum))
}// notify 根据消息类型通知订阅者
func (c *CreditCard) notify(msgType MsgType, message string) {if subs, ok : c.subscriberGroup[msgType]; ok {for _, sub : range subs {sub.Update(message)}}
}
五测试程序 package observerimport testingfunc TestObserver(t *testing.T) {// 创建张三的信用卡creditCard : NewCreditCard(张三)// 短信通知订阅信用卡消费及逾期消息creditCard.Subscribe(new(shortMessage), ConsumeType, ExpireType)// 电子邮件通知订阅信用卡账单及逾期消息creditCard.Subscribe(new(email), BillType, ExpireType)// 电话通知订阅信用卡逾期消息同时逾期消息通过三种方式通知creditCard.Subscribe(new(telephone), ExpireType)creditCard.Consume(500.00) // 信用卡消费creditCard.Consume(800.00) // 信用卡消费creditCard.SendBill() // 信用卡发送账单creditCard.Expire() // 信用卡逾期// 信用卡逾期消息取消电子邮件及短信通知订阅creditCard.Unsubscribe(new(email), ExpireType)creditCard.Unsubscribe(new(shortMessage), ExpireType)creditCard.Consume(300.00) // 信用卡消费creditCard.Expire() // 信用卡逾期
}
六运行结果 RUN TestObserver
通过【手机短息】发送消息:尊敬的持卡人张三,您当前消费500.00元;
通过【手机短息】发送消息:尊敬的持卡人张三,您当前消费800.00元;
通过【电子邮件】发送消息:尊敬的持卡人张三,您本月账单已出消费总额1300.00元;
通过【手机短息】发送消息:尊敬的持卡人张三,您本月账单已逾期请及时还款总额1300.00元;
通过【电子邮件】发送消息:尊敬的持卡人张三,您本月账单已逾期请及时还款总额1300.00元;
通过【电话】告知:尊敬的持卡人张三,您本月账单已逾期请及时还款总额1300.00元;
通过【手机短息】发送消息:尊敬的持卡人张三,您当前消费300.00元;
通过【电话】告知:尊敬的持卡人张三,您本月账单已逾期请及时还款总额1600.00元;
--- PASS: TestObserver (0.00s)
PASS 状态模式
一概念 状态模式是一种行为设计模式让你能在一个对象的内部状态变化时改变其行为使其看上去就像改变了自身所属的类一样。
该模式将与状态相关的行为抽取到独立的状态类中让原对象将工作委派给这些类的实例而不是自行进行处理。
状态迁移有四个元素组成起始状态、触发迁移的事件终止状态以及要执行的动作每个具体的状态包含触发状态迁移的执行方法迁移方法的实现是执行持有状态对象的动作方法同时设置状态为下一个流转状态持有状态的业务对象包含有触发状态迁移方法这些迁移方法将请求委托给当前具体状态对象的迁移方法。
二示例
IPhone手机充电就是一个手机电池状态的流转一开始手机处于有电状态插入充电插头后继续充电到满电状态并进入断电保护拔出充电插头后使用手机由满电逐渐变为没电最终关机
状态迁移表
起始状态触发事件终止状态执行动作有电插入充电线满电充电有电拔出充电线没电耗电满电插入充电线满电停止充电满电拔出充电线有电耗电没电插入充电线有电充电没电拔出充电线没电关机
三电池状态 package stateimport fmt// BatteryState 电池状态接口支持手机充电线插拔事件
type BatteryState interface {ConnectPlug(iPhone *IPhone) stringDisconnectPlug(iPhone *IPhone) string
}// fullBatteryState 满电状态
type fullBatteryState struct{}func (s *fullBatteryState) String() string {return 满电状态
}func (s *fullBatteryState) ConnectPlug(iPhone *IPhone) string {return iPhone.pauseCharge()
}func (s *fullBatteryState) DisconnectPlug(iPhone *IPhone) string {iPhone.SetBatteryState(PartBatteryState)return fmt.Sprintf(%s,%s转为%s, iPhone.consume(), s, PartBatteryState)
}// emptyBatteryState 空电状态
type emptyBatteryState struct{}func (s *emptyBatteryState) String() string {return 没电状态
}func (s *emptyBatteryState) ConnectPlug(iPhone *IPhone) string {iPhone.SetBatteryState(PartBatteryState)return fmt.Sprintf(%s,%s转为%s, iPhone.charge(), s, PartBatteryState)
}func (s *emptyBatteryState) DisconnectPlug(iPhone *IPhone) string {return iPhone.shutdown()
}// partBatteryState 部分电状态
type partBatteryState struct{}func (s *partBatteryState) String() string {return 有电状态
}func (s *partBatteryState) ConnectPlug(iPhone *IPhone) string {iPhone.SetBatteryState(FullBatteryState)return fmt.Sprintf(%s,%s转为%s, iPhone.charge(), s, FullBatteryState)
}func (s *partBatteryState) DisconnectPlug(iPhone *IPhone) string {iPhone.SetBatteryState(EmptyBatteryState)return fmt.Sprintf(%s,%s转为%s, iPhone.consume(), s, EmptyBatteryState)
}
四IPhone手机 package stateimport fmt// 电池状态单例全局统一使用三个状态的单例不需要重复创建
var (FullBatteryState new(fullBatteryState) // 满电EmptyBatteryState new(emptyBatteryState) // 空电PartBatteryState new(partBatteryState) // 部分电
)// IPhone 已手机充电为例实现状态模式
type IPhone struct {model string // 手机型号batteryState BatteryState // 电池状态
}// NewIPhone 创建指定型号手机
func NewIPhone(model string) *IPhone {return IPhone{model: model,batteryState: PartBatteryState,}
}// BatteryState 输出电池当前状态
func (i *IPhone) BatteryState() string {return fmt.Sprintf(iPhone %s 当前为%s, i.model, i.batteryState)
}// ConnectPlug 连接充电线
func (i *IPhone) ConnectPlug() string {return fmt.Sprintf(iPhone %s 连接电源线,%s, i.model, i.batteryState.ConnectPlug(i))
}// DisconnectPlug 断开充电线
func (i *IPhone) DisconnectPlug() string {return fmt.Sprintf(iPhone %s 断开电源线,%s, i.model, i.batteryState.DisconnectPlug(i))
}// SetBatteryState 设置电池状态
func (i *IPhone) SetBatteryState(state BatteryState) {i.batteryState state
}func (i *IPhone) charge() string {return 正在充电
}func (i *IPhone) pauseCharge() string {return 电已满,暂停充电
}func (i *IPhone) shutdown() string {return 手机关闭
}func (i *IPhone) consume() string {return 使用中,消耗电量
}
五测试程序 package stateimport (fmttesting
)func TestState(t *testing.T) {iPhone13Pro : NewIPhone(13 pro) // 刚创建的手机有部分电fmt.Println(iPhone13Pro.BatteryState()) // 打印部分电状态fmt.Println(iPhone13Pro.ConnectPlug()) // 插上电源插头继续充满电fmt.Println(iPhone13Pro.ConnectPlug()) // 满电后再充电会触发满电保护fmt.Println(iPhone13Pro.DisconnectPlug()) // 拔掉电源使用手机消耗电量变为有部分电fmt.Println(iPhone13Pro.DisconnectPlug()) // 一直使用手机直到没电fmt.Println(iPhone13Pro.DisconnectPlug()) // 没电后会关机fmt.Println(iPhone13Pro.ConnectPlug()) // 再次插上电源一会变为有电状态
}
六运行结果 RUN TestState
iPhone 13 pro 当前为有电状态
iPhone 13 pro 连接电源线,正在充电,有电状态转为满电状态
iPhone 13 pro 连接电源线,电已满,暂停充电
iPhone 13 pro 断开电源线,使用中,消耗电量,满电状态转为有电状态
iPhone 13 pro 断开电源线,使用中,消耗电量,有电状态转为没电状态
iPhone 13 pro 断开电源线,手机关闭
iPhone 13 pro 连接电源线,正在充电,没电状态转为有电状态
--- PASS: TestState (0.00s)
PASS 策略模式
一概念
策略模式是一种行为设计模式它能让你定义一系列算法并将每种算法分别放入独立的类中以使算法的对象能够相互替换。
原始对象被称为上下文它包含指向策略对象的引用并将执行行为的任务分派给策略对象。为了改变上下文完成其工作的方式其他对象可以使用另一个对象来替换当前链接的策略对象。
策略模式是最常用的设计模式也是比较简单的设计模式是以多态替换条件表达式重构方法的具体实现是面向接口编程原则的最直接体现
二示例
北京是一个四季分明的城市每个季节天气情况都有明显特点我们定义一个显示天气情况的季节接口具体的四季实现都会保存一个城市和天气情况的映射表城市对象会包含季节接口随着四季的变化天气情况也随之变化
三四季天气 package strategyimport fmt// Season 季节的策略接口不同季节表现得天气不同
type Season interface {ShowWeather(city string) string // 显示指定城市的天气情况
}type spring struct {weathers map[string]string // 存储不同城市春天气候
}func NewSpring() *spring {return spring{weathers: map[string]string{北京: 干燥多风, 昆明: 清凉舒适},}
}func (s *spring) ShowWeather(city string) string {return fmt.Sprintf(%s的春天%s;, city, s.weathers[city])
}type summer struct {weathers map[string]string // 存储不同城市夏天气候
}func NewSummer() *summer {return summer{weathers: map[string]string{北京: 高温多雨, 昆明: 清凉舒适},}
}func (s *summer) ShowWeather(city string) string {return fmt.Sprintf(%s的夏天%s;, city, s.weathers[city])
}type autumn struct {weathers map[string]string // 存储不同城市秋天气候
}func NewAutumn() *autumn {return autumn{weathers: map[string]string{北京: 凉爽舒适, 昆明: 清凉舒适},}
}func (a *autumn) ShowWeather(city string) string {return fmt.Sprintf(%s的秋天%s;, city, a.weathers[city])
}type winter struct {weathers map[string]string // 存储不同城市冬天气候
}func NewWinter() *winter {return winter{weathers: map[string]string{北京: 干燥寒冷, 昆明: 清凉舒适},}
}func (w *winter) ShowWeather(city string) string {return fmt.Sprintf(%s的冬天%s;, city, w.weathers[city])
}
四城市气候 package strategyimport (fmt
)// City 城市
type City struct {name stringfeature stringseason Season
}// NewCity 根据名称及季候特征创建城市
func NewCity(name, feature string) *City {return City{name: name,feature: feature,}
}// SetSeason 设置不同季节类似天气在不同季节的不同策略
func (c *City) SetSeason(season Season) {c.season season
}// String 显示城市的气候信息
func (c *City) String() string {return fmt.Sprintf(%s%s%s, c.name, c.feature, c.season.ShowWeather(c.name))
}
五测试程序 package strategyimport (fmttesting
)func TestStrategy(t *testing.T) {Beijing : NewCity(北京, 四季分明)Beijing.SetSeason(NewSpring())fmt.Println(Beijing)Beijing.SetSeason(NewSummer())fmt.Println(Beijing)Beijing.SetSeason(NewAutumn())fmt.Println(Beijing)Beijing.SetSeason(NewWinter())fmt.Println(Beijing)
}
六运行结果 RUN TestStrategy
北京四季分明北京的春天干燥多风;
北京四季分明北京的夏天高温多雨;
北京四季分明北京的秋天凉爽舒适;
北京四季分明北京的冬天干燥寒冷;
--- PASS: TestStrategy (0.00s)
PASS 模板方法模式
一概念 模板方法模式是一种行为设计模式它在超类中定义了一个算法的框架允许子类在不修改结构的情况下重写算法的特定步骤。
由于GO语言没有继承的语法模板方法又是依赖继承实现的设计模式因此GO语言实现模板方法比较困难 GO语言支持隐式内嵌字段“继承”其他结构体的字段与方法但是这个并不是真正意义上的继承语法外层结构重写隐式字段中的算法特定步骤后无法动态绑定到“继承”过来的算法的框架方法调用中因此不能实现模板方法模式的语义。
二示例
本示例给出一种间接实现模板方法的方式也比较符合模板方法模式的定义
将多个算法特定步骤组合成一个接口基类隐式内嵌算法步骤接口同时调用算法步骤接口的各方法实现算法的模板方法此时基类内嵌的算法步骤接口并没有真正的处理行为子类隐式内嵌基类并覆写算法步骤接口的方法通过工厂方法创建具体子类并将自己的引用赋值给基类中算法步骤接口字段
以演员装扮为例演员的装扮是分为化妆穿衣配饰三步骤三个步骤又根据不同角色的演员有所差别因此演员基类实现装扮的模板方法对于化妆穿衣配饰的三个步骤在子类演员中具体实现子类具体演员分为男演员、女演员和儿童演员
三演员基类 package templatemethodimport (bytesfmt
)// IActor 演员接口
type IActor interface {DressUp() string // 装扮
}// dressBehavior 装扮的多个行为这里多个行为是私有的通过DressUp模版方法调用
type dressBehavior interface {makeUp() string // 化妆clothe() string // 穿衣wear() string // 配饰
}// BaseActor 演员基类
type BaseActor struct {roleName string // 扮演角色dressBehavior // 装扮行为
}// DressUp 统一实现演员接口的DressUp模版方法装扮过程通过不同装扮行为进行扩展
func (b *BaseActor) DressUp() string {buf : bytes.Buffer{}buf.WriteString(fmt.Sprintf(扮演%s的, b.roleName))buf.WriteString(b.makeUp())buf.WriteString(b.clothe())buf.WriteString(b.wear())return buf.String()
}
四具体演员 package templatemethod// womanActor 扩展装扮行为的女演员
type womanActor struct {BaseActor
}// NewWomanActor 指定角色创建女演员
func NewWomanActor(roleName string) *womanActor {actor : new(womanActor) // 创建女演员actor.roleName roleName // 设置角色actor.dressBehavior actor // 将女演员实现的扩展装扮行为设置给自己的装扮行为接口return actor
}// 化妆
func (w *womanActor) makeUp() string {return 女演员涂着口红画着眉毛
}// 穿衣
func (w *womanActor) clothe() string {return 穿着连衣裙
}// 配饰
func (w *womanActor) wear() string {return 带着耳环手拎着包
}// manActor 扩展装扮行为的男演员
type manActor struct {BaseActor
}func NewManActor(roleName string) *manActor {actor : new(manActor)actor.roleName roleNameactor.dressBehavior actor // 将男演员实现的扩展装扮行为设置给自己的装扮行为接口return actor
}func (m *manActor) makeUp() string {return 男演员刮净胡子抹上发胶
}func (m *manActor) clothe() string {return 穿着一身西装
}func (m *manActor) wear() string {return 带上手表抽着烟
}// NewChildActor 扩展装扮行为的儿童演员
type childActor struct {BaseActor
}func NewChildActor(roleName string) *childActor {actor : new(childActor)actor.roleName roleNameactor.dressBehavior actor // 将儿童演员实现的扩展装扮行为设置给自己的装扮行为接口return actor
}func (c *childActor) makeUp() string {return 儿童演员抹上红脸蛋
}func (c *childActor) clothe() string {return 穿着一身童装
}func (c *childActor) wear() string {return 手里拿着一串糖葫芦
}五测试程序 package templatemethodimport (fmttesting
)func TestTemplateMethod(t *testing.T) {showActors(NewWomanActor(妈妈), NewManActor(爸爸), NewChildActor(儿子))
}// showActors 显示演员的装扮信息
func showActors(actors ...IActor) {for _, actor : range actors {fmt.Println(actor.DressUp())}
}
六运行结果 RUN TestTemplateMethod
扮演妈妈的女演员涂着口红画着眉毛穿着连衣裙带着耳环手拎着包
扮演爸爸的男演员刮净胡子抹上发胶穿着一身西装带上手表抽着烟
扮演儿子的儿童演员抹上红脸蛋穿着一身童装手里拿着一串糖葫芦
--- PASS: TestTemplateMethod (0.00s)
PASS 访问者模式
一概念
访问者模式是一种行为设计模式它能将算法与其所作用的对象隔离开来。允许你在不修改已有代码的情况下向已有类层次结构中增加新的行为。
访问者接口需要根据被访问者具体类定义多个相似的访问方法每个具体类对应一个访问方法每个被访问者需要实现一个接受访问者对象的方法方法的实现就是去调用访问者接口对应该类的访问方法这个接受方法可以传入不同目的访问者接口的具体实现从而在不修改被访问对象的前提下增加新的功能
二示例
公司中存在多种类型的员工包括产品经理、软件工程师、人力资源等他们的KPI指标不尽相同产品经理为上线产品数量及满意度软件工程师为实现的需求数及修改bug数人力资源为招聘员工的数量公司要根据员工完成的KPI进行表彰公示同时根据KPI完成情况定薪酬这些功能都是员工类职责之外的不能修改员工本身的类我们通过访问者模式实现KPI表彰排名及薪酬发放
三员工结构 package visitorimport fmt// Employee 员工接口
type Employee interface {KPI() string // 完成kpi信息Accept(visitor EmployeeVisitor) // 接受访问者对象
}// productManager 产品经理
type productManager struct {name string // 名称productNum int // 上线产品数satisfaction int // 平均满意度
}func NewProductManager(name string, productNum int, satisfaction int) *productManager {return productManager{name: name,productNum: productNum,satisfaction: satisfaction,}
}func (p *productManager) KPI() string {return fmt.Sprintf(产品经理%s上线%d个产品平均满意度为%d, p.name, p.productNum, p.satisfaction)
}func (p *productManager) Accept(visitor EmployeeVisitor) {visitor.VisitProductManager(p)
}// softwareEngineer 软件工程师
type softwareEngineer struct {name string // 姓名requirementNum int // 完成需求数bugNum int // 修复问题数
}func NewSoftwareEngineer(name string, requirementNum int, bugNum int) *softwareEngineer {return softwareEngineer{name: name,requirementNum: requirementNum,bugNum: bugNum,}
}func (s *softwareEngineer) KPI() string {return fmt.Sprintf(软件工程师%s完成%d个需求修复%d个问题, s.name, s.requirementNum, s.bugNum)
}func (s *softwareEngineer) Accept(visitor EmployeeVisitor) {visitor.VisitSoftwareEngineer(s)
}// hr 人力资源
type hr struct {name string // 姓名recruitNum int // 招聘人数
}func NewHR(name string, recruitNum int) *hr {return hr{name: name,recruitNum: recruitNum,}
}func (h *hr) KPI() string {return fmt.Sprintf(人力资源%s招聘%d名员工, h.name, h.recruitNum)
}func (h *hr) Accept(visitor EmployeeVisitor) {visitor.VisitHR(h)
}
四员工访问者 package visitorimport (fmtsort
)// EmployeeVisitor 员工访问者接口
type EmployeeVisitor interface {VisitProductManager(pm *productManager) // 访问产品经理VisitSoftwareEngineer(se *softwareEngineer) // 访问软件工程师VisitHR(hr *hr) // 访问人力资源
}// kpi kpi对象
type kpi struct {name string // 完成kpi姓名sum int // 完成kpi总数量
}// kpiTopVisitor 员工kpi排名访问者
type kpiTopVisitor struct {top []*kpi
}func (k *kpiTopVisitor) VisitProductManager(pm *productManager) {k.top append(k.top, kpi{name: pm.name,sum: pm.productNum pm.satisfaction,})
}func (k *kpiTopVisitor) VisitSoftwareEngineer(se *softwareEngineer) {k.top append(k.top, kpi{name: se.name,sum: se.requirementNum se.bugNum,})
}func (k *kpiTopVisitor) VisitHR(hr *hr) {k.top append(k.top, kpi{name: hr.name,sum: hr.recruitNum,})
}// Publish 发布KPI排行榜
func (k *kpiTopVisitor) Publish() {sort.Slice(k.top, func(i, j int) bool {return k.top[i].sum k.top[j].sum})for i, curKPI : range k.top {fmt.Printf(第%d名%s完成KPI总数%d\n, i1, curKPI.name, curKPI.sum)}
}// salaryVisitor 薪酬访问者
type salaryVisitor struct{}func (s *salaryVisitor) VisitProductManager(pm *productManager) {fmt.Printf(产品经理基本薪资1000元KPI单位薪资100元)fmt.Printf(%s总工资为%d元\n, pm.KPI(), (pm.productNumpm.satisfaction)*1001000)
}func (s *salaryVisitor) VisitSoftwareEngineer(se *softwareEngineer) {fmt.Printf(软件工程师基本薪资1500元KPI单位薪资80元)fmt.Printf(%s总工资为%d元\n, se.KPI(), (se.requirementNumse.bugNum)*801500)
}func (s *salaryVisitor) VisitHR(hr *hr) {fmt.Printf(人力资源基本薪资800元KPI单位薪资120元)fmt.Printf(%s总工资为%d元\n, hr.KPI(), hr.recruitNum*120800)
}
五测试程序 package visitorimport testingfunc TestVisitor(t *testing.T) {allEmployees : AllEmployees() // 获取所有员工kpiTop : new(kpiTopVisitor) // 创建KPI排行访问者VisitAllEmployees(kpiTop, allEmployees)kpiTop.Publish() // 发布排行榜salary : new(salaryVisitor) // 创建薪酬访问者VisitAllEmployees(salary, allEmployees)
}// VisitAllEmployees 遍历所有员工调用访问者
func VisitAllEmployees(visitor EmployeeVisitor, allEmployees []Employee) {for _, employee : range allEmployees {employee.Accept(visitor)}
}// AllEmployees 获得所有公司员工
func AllEmployees() []Employee {var employees []Employeeemployees append(employees, NewHR(小明, 10))employees append(employees, NewProductManager(小红, 4, 7))employees append(employees, NewSoftwareEngineer(张三, 10, 5))employees append(employees, NewSoftwareEngineer(李四, 3, 6))employees append(employees, NewSoftwareEngineer(王五, 7, 1))return employees
}
六运行结果 RUN TestVisitor
第1名张三完成KPI总数15
第2名小红完成KPI总数11
第3名小明完成KPI总数10
第4名李四完成KPI总数9
第5名王五完成KPI总数8
人力资源基本薪资800元KPI单位薪资120元人力资源小明招聘10名员工总工资为2000元
产品经理基本薪资1000元KPI单位薪资100元产品经理小红上线4个产品平均满意度为7总工资为2100元
软件工程师基本薪资1500元KPI单位薪资80元软件工程师张三完成10个需求修复5个问题总工资为2700元
软件工程师基本薪资1500元KPI单位薪资80元软件工程师李四完成3个需求修复6个问题总工资为2220元
软件工程师基本薪资1500元KPI单位薪资80元软件工程师王五完成7个需求修复1个问题总工资为2140元
--- PASS: TestVisitor (0.00s)