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"
}
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())
}
二、原型模式
适用场景:一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用
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
}
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)
}
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("不支持改播放格式")
}
}
七、观察者模式
观察者模式也是我们日常比较常接触到的设计模式之一。在一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
常见:数据库修改一个模型的数据,与其关联的多张表也需要同步修改,这时就可以使用观察者模式进行广播,通知其他模型进行修改数据。
之前做android开发经常会接触到notify这类似的方法,这就是观察者模式实现,使用之前在全局注册一个监听,触发某个事件时会通知所有的监听对象进行修改。
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)
}
// 监听接口
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)
}
func Try(){
project := &Project{}
NewProjectNewsSync(project)
NewProjectSync(project)
// 修改城市为1,附属观察者触发监听
project.setCity(1)
// 修改城市为2,附属观察者触发监听
project.setCity(2)
}