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

2021/6/28 23:52:27

本文主要是介绍【Golang设计模式】状态、原型、享元、组合、代理、适配器模式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

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("不支持改播放格式")
    	}
    }
    


这篇关于【Golang设计模式】状态、原型、享元、组合、代理、适配器模式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程