【Golang设计模式】状态、原型、享元、组合、代理、适配器、观察者模式

一、状态模式

可以将枚举的类型(比如:停止和开始),将这两种状态封装为各自的对象,封装对象可以抽象独立。多个对象可以共享一个状态对象,从而将系统中的各个状态抽象化。

  1. 状态接口
    type State interface {
    	doAction(context *Context)
    	toString() string
    }
    
  2. 状态实例
    // ==== 开始 ====
    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"
    }
    
  3. 上下文对象
    全文可以共享一个上下文对象,修改单例Context中的状态,即可改变当前全局对象。
    type Context struct {
    	state State
    }
    func (this *Context) setState(state State) {
    	this.state = state;
    }
    func (this *Context) getState() State {
    	return this.state;
    }
    
  4. 测试
    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())
    }
    

二、原型模式

适用场景:一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用

  1. 实现原型模式的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
    }
    // ===================
    
  2. 测试
    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()
    }
    

三、享元模式

这里模式类似是数据库连接池,例如我们需要一个资源需要创建,但是创建完之后我们不去处理就会被清理掉,这样就会相当消耗资源,创建资源并没有充分利用。
上述情况,我们可以这么做:如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面
需要注意:这些类必须有一个工厂对象加以控制,以防止引起线程安全的问题

  1. 随便写一个对象
    type User struct {
    	name string
    }
    
  2. 享元核心
    将创建的对象存储在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
    }
    
  3. 测试
    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()
    }
    
  4. 输出
    在这里插入图片描述

四、组合模式

这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
演示一个组织中员工的层次结构

  1. 雇员的基本结构体

    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{},
    	}
    }
    
  2. 测试

    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()
    }
    

五、代理模式

可以想象成:买火车票不一定在火车站买,也可以去代售点(就是代理处理某些事务的类)

  1. 代理模式
    // 先初始化总代理
    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()
    }
    
  2. 测试
    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格式的音频,但是又不想重构,怎么办?

适配其他播放格式

  1. 增加解析格式
    // 高级媒体播放器
    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)
    }
    
  2. 添加适配器
    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
    }
    
  3. 播放更多格式音频
    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("不支持改播放格式")
    	}
    }
    

七、观察者模式

观察者模式也是我们日常比较常接触到的设计模式之一。在一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
常见:数据库修改一个模型的数据,与其关联的多张表也需要同步修改,这时就可以使用观察者模式进行广播,通知其他模型进行修改数据。


之前做android开发经常会接触到notify这类似的方法,这就是观察者模式实现,使用之前在全局注册一个监听,触发某个事件时会通知所有的监听对象进行修改。

  1. 被观察的模型
    type Project struct {
    	id int
    	name_cn string
    	city_id int
    	listeners []listener
    }
    // 修改City_id时通知所有观察者
    func (this *Project) setCity(city_id int) {
    	this.city_id = city_id
    	// 通知观察者
    	this.notifyAllProjectSync()
    }
    func (this *Project) notifyAllProjectSync()  {
    	for _, listener := range this.listeners {
    		listener.onListenerProjectUpdate()
    	}
    }
    // 注册观察到监听类
    func (this *Project) addListener(listener listener)  {
    	this.listeners = append(this.listeners, listener)
    }
    
  2. 观察者注册
    // 监听接口
    type listener interface {
    	onListenerProjectUpdate()
    }
    
    // ==== 项目异步类 =====
    type ProjectSync struct {
    	region_leader_id int
    	*Project
    }
    // 新建时注册到Project,监听Project修改
    func NewProjectSync(project Project) {
    	projectSync := &ProjectSync{region_leader_id: 0}
    	project.addListener(projectSync)
    }
    // 注册监听Project修改,可以做处理一些相关类的触发修改机制
    func (this *ProjectSync) onListenerProjectUpdate()  {
    	if this.city_id == 1 {
    		this.region_leader_id = 1
    	} else {
    		this.region_leader_id = 2
    	}
    	fmt.Println("ProjectSync触发,完成修改region_leader_id,当前值:",this.region_leader_id)
    }
    
    // ==== 资源异步类 ====
    type ProjectNewsSync struct {
    	region_leader_id int
    	*Project
    }
    // 新建时注册到Project,监听Project修改
    func NewProjectNewsSync(project Project) {
    	projectNews := &ProjectNewsSync{region_leader_id: 0}
    	project.addListener(projectNews)
    }
    // 注册监听Project修改,可以做处理一些相关类的触发修改机制
    func (this *ProjectNewsSync) onListenerProjectUpdate()  {
    	if this.city_id == 1 {
    		this.region_leader_id = 1
    	} else {
    		this.region_leader_id = 2
    	}
    	fmt.Println("ProjectNewsSync触发,完成修改region_leader_id,当前值:",this.region_leader_id)
    }
    
  3. 测试
    func Try(){
    	project := &Project{}
    	
    	NewProjectNewsSync(project)
    	NewProjectSync(project)
    
    	// 修改城市为1,附属观察者触发监听
    	project.setCity(1)
    	// 修改城市为2,附属观察者触发监听
    	project.setCity(2)
    }
    
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鱼小鱼啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值