【Golang设计模式】状态、原型、享元、组合、代理、适配器模式
2021/6/28 23:52:27
本文主要是介绍【Golang设计模式】状态、原型、享元、组合、代理、适配器模式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
golang设计模式
- 一、状态模式
- 二、原型模式
- 三、享元模式
- 四、组合模式
- 五、代理模式
- 六、适配器模式
- 默认播放器
- 适配其他播放格式
一、状态模式
可以将枚举的类型(比如:停止和开始),将这两种状态封装为各自的对象,封装对象可以抽象独立。多个对象可以共享一个状态对象,从而将系统中的各个状态抽象化。
- 状态接口
type State interface { doAction(context *Context) toString() string }
- 状态实例
// ==== 开始 ==== type StartState struct {} func (this *StartState) doAction(context *Context) { fmt.Println("开始--- 这里可以做修改状态的逻辑操作") context.setState(this) } func (this *StartState) toString() string { return "Start State" } // ==== 停止 ==== type StopState struct {} func (this *StopState) doAction(context *Context) { fmt.Println("结束--- 这里可以做修改状态的逻辑操作") context.setState(this) } func (this *StopState) toString() string { return "Stop State" }
- 上下文对象
全文可以共享一个上下文对象,修改单例Context中的状态,即可改变当前全局对象。type Context struct { state State } func (this *Context) setState(state State) { this.state = state; } func (this *Context) getState() State { return this.state; }
- 测试
func Try() { context := &Context { state: nil, } // 修改当前状态 start := &StartState{} start.doAction(context) fmt.Println(context.getState().toString()) // 修改当前状态 stop := &StopState{} stop.doAction(context) fmt.Println(context.getState().toString()) }
二、原型模式
适用场景:一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用
- 实现原型模式的
clone()
方法type User struct { name string age int sex int } type UserExt struct { synopsis string // 简介 } type UserInfo struct { User UserExt } func (this *UserInfo) setUser(name string, age int, sex int) { this.name = name this.age = age this.sex = sex } func (this *UserExt) setUserExt(synopsis string) { this.synopsis = synopsis } func (this *UserInfo) display() { fmt.Println("个人信息:", this.name, this.age, "简介:", this.synopsis) } // ==== 这里是核心 ==== func (this *UserInfo) clone() *UserInfo { // 将UserInfo对象赋值给newUserInfo newUserInfo := *this // 这里就可以返回一个新的UserInfo对象 return &newUserInfo } // ===================
- 测试
func Try() { user1 := &UserInfo{} user1.setUser("张三", 90, 1) user1.setUserExt("这是张三简历简介") user2 := user1.clone() user2.setUser("李四", 19, 2) user2.setUserExt("这是李四简历简介") user3 := user1.clone() user3.setUser("赵六", 20, 1) user3.setUserExt("这是赵六简历简介") user1.display() user2.display() user3.display() }
三、享元模式
这里模式类似是
数据库连接池
,例如我们需要一个资源需要创建,但是创建完之后我们不去处理就会被清理掉,这样就会相当消耗资源,创建资源并没有充分利用。
上述情况,我们可以这么做:如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面
需要注意:这些类必须有一个工厂对象加以控制,以防止引起线程安全的问题
- 随便写一个对象
type User struct { name string }
- 享元核心
将创建的对象存储在MAP中,没有则创建,并且返回实例,可全局共享(getUser方法为了防止线程安全的问题,所以加锁)var mutex sync.Mutex type UserFactory struct { userMap map[string]*User } func (this *UserFactory) showKeys() { for key := range this.userMap { fmt.Println(key) } } // 多线程可获取 func (this *UserFactory) getUser(name string) User { user, ok := this.userMap[name] // 此处防止多线程获取,会创建多个相同实例,使用双重锁,避免每次加锁 if !ok { mutex.Lock() defer mutex.Unlock() if _, ok = this.userMap[name]; !ok { user = &User{name: name} this.userMap[name] = user } } return *user }
- 测试
func Try() { userFactory := UserFactory{ userMap: map[string]*User{}, } fmt.Println(userFactory.getUser("张三")) fmt.Println(userFactory.getUser("张三")) fmt.Println(userFactory.getUser("张三")) fmt.Println(userFactory.getUser("李四")) fmt.Println(userFactory.getUser("赵六")) userFactory.showKeys() }
- 输出
四、组合模式
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
演示一个组织中员工的层次结构
-
雇员的基本结构体
type Employee struct { name string dept string salary int // 这个切片存储下属信息 subordinates []*Employee } // 添加下属 func (this *Employee) addSubordinate(emp *Employee) { this.subordinates = append(this.subordinates, emp) } func (this *Employee) showEmps() { for index, item := range this.subordinates { fmt.Println(index, item) } } func NewEmployee(name string, dept string, salary int) *Employee { return &Employee{ name: name, dept: dept, salary: salary, subordinates: []*Employee{}, } }
-
测试
func Try() { // 最大的CEO ceo := NewEmployee("jack", "CEO", 2000000) // 经理 manger1 := NewEmployee("tom", "manager", 10000) manger2 := NewEmployee("jerry", "manager", 10000) manger3 := NewEmployee("less", "manager", 10000) manger4 := NewEmployee("bob", "manager", 10000) // ceo的下属是经理 ceo.addSubordinate(manger1) ceo.addSubordinate(manger2) ceo.addSubordinate(manger3) ceo.addSubordinate(manger4) // 基础雇员 emp1 := NewEmployee("deng1", "employee", 2000) emp2 := NewEmployee("deng2", "employee", 2000) emp3 := NewEmployee("deng3", "employee", 2000) emp4 := NewEmployee("deng4", "employee", 2000) emp5 := NewEmployee("deng5", "employee", 2000) emp6 := NewEmployee("deng6", "employee", 2000) emp7 := NewEmployee("deng7", "employee", 2000) // 给经理添加下属 manger1.addSubordinate(emp1) manger1.addSubordinate(emp2) manger1.addSubordinate(emp3) manger2.addSubordinate(emp4) manger2.addSubordinate(emp5) manger3.addSubordinate(emp6) manger3.addSubordinate(emp7) ceo.showEmps() }
五、代理模式
可以想象成:买火车票不一定在火车站买,也可以去代售点(就是代理处理某些事务的类)
- 代理模式
// 先初始化总代理 var proxyLeader = &ProxyLeader{ TicketNumber: 1000, } // 总代理 type ProxyLeader struct { TicketNumber int } func (this *ProxyLeader) BuyTicket() { this.TicketNumber-- } // 分代理 type ProxyBranch struct { leader *ProxyLeader TicketNumber int } func (this *ProxyBranch) BuyTicket() { if this.leader == nil { this.leader = proxyLeader this.TicketNumber = this.leader.TicketNumber } // 代理只减去库存 this.TicketNumber-- // 购买的职责交给总代理 this.leader.BuyTicket() }
- 测试
func Try() { branch := &ProxyBranch{ } branch.BuyTicket() // 输出代理库存 fmt.Println(branch.TicketNumber) fmt.Println(branch.leader.TicketNumber) }
六、适配器模式
形象的例子1:美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V
形象的例子2:音频播放器自带的支持mp3格式,此时需要对硬件升级,兼容适配mp4、vlc格式的,就需要对原本的播放器增加适配器兼容新的格式。
默认播放器
type MediaPlayer interface { play(fileName string) } type AudioPlayer struct {} func (this *AudioPlayer) play(fileName string) { fmt.Println("播放mp3格式,文件名称:", fileName) } func Try() { player := AudioPlayer{} player.play("测试1.mp3") }
现在我们需要,支持mp4、vlc格式的音频,但是又不想重构,怎么办?
适配其他播放格式
- 增加解析格式
// 高级媒体播放器 type AdvancedMediaPlayer interface { playVlc(fileName string) playMp4(fileName string) } // vlc格式播放器 type VlcPlayer struct {} func (this *VlcPlayer) playVlc(fileName string) { fmt.Println("播放vlc格式,文件名称:", fileName) } func (this *VlcPlayer) playMp4(fileName string) { } // mp4格式 type Mp4Player struct {} func (this *Mp4Player) playVlc(fileName string) { } func (this *Mp4Player) playMp4(fileName string) { fmt.Println("播放mp4格式,文件名称:", fileName) }
- 添加适配器
MediaAdapter
本质还是 MediaPlayer
只不过MediaAdapter
是为适配更多格式type MediaAdapter struct { AdvancedMediaPlayer } func (this *MediaAdapter) play(fileName string) { if strings.HasSuffix(fileName, ".vlc") { this.playVlc(fileName) } else if strings.HasSuffix(fileName, ".mp4") { this.playMp4(fileName) } } // 根据文件后缀,返回对应结构体 func NewMediaAdapter(fileName string) *MediaAdapter { adpter := &MediaAdapter{} if strings.HasSuffix(fileName, ".vlc") { adpter.AdvancedMediaPlayer = &VlcPlayer{} } else if strings.HasSuffix(fileName, ".mp4") { adpter.AdvancedMediaPlayer = &Mp4Player{} } return adpter }
- 播放更多格式音频
type MediaPlayer interface { play(fileName string) } type AudioPlayer struct { MediaPlayer } func (this *AudioPlayer) play(fileName string) { if strings.HasSuffix(fileName, ".mp3") { // mp3 也可抽出为适配器,方便扩展,方便统一管理 fmt.Println("播放mp3格式,文件名称:", fileName) } else if strings.HasSuffix(fileName, ".vlc") || strings.HasSuffix(fileName, ".mp4") { this.MediaPlayer = NewMediaAdapter(fileName) this.MediaPlayer.play(fileName) } else { fmt.Println("不支持改播放格式") } }
这篇关于【Golang设计模式】状态、原型、享元、组合、代理、适配器模式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-02MongoDB项目实战:从入门到初级应用
- 2024-11-01随时随地一键转录,Google Cloud 新模型 Chirp 2 让语音识别更上一层楼
- 2024-10-25Google Cloud动手实验详解:如何在Cloud Run上开发无服务器应用
- 2024-10-24AI ?先驱齐聚 BAAI 2024,发布大规模语言、多模态、具身、生物计算以及 FlagOpen 2.0 等 AI 模型创新成果。
- 2024-10-20goland工具下,如修改一个项目的标准库SDK的版本-icode9专业技术文章分享
- 2024-10-17Go学习:初学者的简单教程
- 2024-10-17Go学习:新手入门完全指南
- 2024-10-17Golang学习:初学者入门教程
- 2024-10-17Golang学习:新手入门教程
- 2024-10-17Gozero学习指南:初学者必备教程