面向对象行为型模式(go语言)

第四部分

  • 责任链模式
    • 纯的模式
    • 不纯的模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
    • 推模型
    • 拉模型
  • 状态模式
  • 策略模式
  • 模板模式
  • 访问者模式

责任链模式

类似击鼓传花,为收到的请求创建一个接受者对象的链,避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,
将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

优点:
1、降低耦合度。
2、对象不需要知道链的结构。
3、通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
4、增加新的请求处理类很方便。

缺点:
1、不能保证请求一定被接收。
2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
3、可能不容易观察运行时的特征,有碍于除错。

Handler 里面聚合自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 setnext。

纯的模式

一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理或把责任推给下家处理。

不纯的模式

允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。

func main() {
	classAdviser := &ClassAdviser{}     // 班主任
	departmentHead := &DepartmentHead{} // 学院主任
	dean := &Dean{}                     // 院长
	deanOfStudies := &DeanOfStudies{}   // 教务处

	classAdviser.setNext(departmentHead)
	departmentHead.setNext(dean)
	dean.setNext(deanOfStudies)

	classAdviser.handleRequest(1)
	classAdviser.handleRequest(7)
	classAdviser.handleRequest(12)
	classAdviser.handleRequest(20)
}

type leader interface {
	// 处理请求
	handleRequest(leaveDays int)
}

// Leader 领导类
type Leader struct {
	next leader
}
func (receiver *Leader) setNext(next leader) {
	receiver.next = next
}
func (receiver *Leader) getNext() leader {
	return receiver.next
}

// ClassAdviser 班主任
type ClassAdviser struct {
	Leader
}
func (receiver *ClassAdviser) handleRequest(leaveDays int) {
	if leaveDays <= 2 {
		fmt.Println("班主任批准请假:", leaveDays, "天")
	} else {
		if receiver.getNext() != nil {
			receiver.getNext().handleRequest(leaveDays)
		} else {
			fmt.Println("天数过多,不予批准")
		}
	}
}

// DepartmentHead 学院主任
type DepartmentHead struct {
	Leader
}
func (receiver *DepartmentHead) handleRequest(leaveDays int) {
	if leaveDays <= 7 {
		fmt.Println("学院主任批准请假:", leaveDays, "天")
	} else {
		if receiver.getNext() != nil {
			receiver.getNext().handleRequest(leaveDays)
		} else {
			fmt.Println("天数过多,不予批准")
		}
	}
}

// Dean 院长
type Dean struct {
	Leader
}
func (receiver *Dean) handleRequest(leaveDays int) {
	if leaveDays <= 10 {
		fmt.Println("院长批准请假:", leaveDays, "天")
	} else {
		if receiver.getNext() != nil {
			receiver.getNext().handleRequest(leaveDays)
		} else {
			fmt.Println("天数过多,不予批准")
		}
	}
}

// DeanOfStudies 教务处
type DeanOfStudies struct {
	Leader
}
func (receiver *DeanOfStudies) handleRequest(leaveDays int) {
	if leaveDays <= 15 {
		fmt.Println("教务处批准请假:", leaveDays, "天")
	} else {
		if receiver.getNext() != nil {
			receiver.getNext().handleRequest(leaveDays)
		} else {
			fmt.Println("天数过多,不予批准")
		}
	}
}
/*
班主任批准请假: 1 天
学院主任批准请假: 7 天
教务处批准请假: 12 天
天数过多,不予批准

命令模式

把一个请求命令封装成一个对象,从而可以用不同的请求对客户进行参数化,将一组行为抽象为对象,可以实现二者之间的松耦合。

优点:
1、降低了系统耦合度。
2、新的命令可以很容易添加到系统中去。

缺点:使用命令模式可能会导致某些系统有过多的具体命令类。

invoker 使用命令对象的入口 received 真正的命令执行对象 Command

面向对象行为型模式(go语言)_第1张图片

func main() {
	Control = NewremoteControl()
	light := &light{}
	Control.setSwitch(light)
	Control.openSwitch()
	Control.offSwitch()
}

var Control *remoteControl

type Switch interface {
	Onopen()
	Onoff()
}

type light struct {
}
type TV struct {
}
type voiceBox struct {
}

// 遥控器
type remoteControl struct {
	cmd Switch
}

func (receiver *light) Onopen() {
	fmt.Println("打开 电灯")
}
func (receiver *light) Onoff() {
	fmt.Println("关闭 电灯")
}
func (receiver *TV) Onopen() {
	fmt.Println("打开 电视")
}
func (receiver *TV) Onoff() {
	fmt.Println("关闭 电视")
}
func (receiver *voiceBox) Onopen() {
	fmt.Println("打开音箱")
}
func (receiver *voiceBox) Onoff() {
	fmt.Println("关闭音箱")
}

func (receiver *remoteControl) setSwitch(p Switch) {
	receiver.cmd = p
}
func (receiver *remoteControl) openSwitch() {
	receiver.cmd.Onopen()
}
func (receiver *remoteControl) offSwitch() {
	receiver.cmd.Onoff()
}
func NewremoteControl() *remoteControl {
	return new(remoteControl)
}
/*
打开 电灯
关闭 电灯

解释器模式

解释器就是解释定义的一种语言的表示,像SQL解析,表达式符号处理引擎等。
再比如一句英文,我们找主谓宾一样,对语句进行解析理解。

优点:
1、可扩展性比较好,灵活。
2、增加了新的解释表达式的方式。

缺点:
1、可利用场景比较少。
2、对于复杂的文法比较难维护。
3、解释器模式会引起类膨胀。
4、解释器模式采用递归调用方法。

func main() {
	add := new(Add)
	pow := new(Pow)
	sub := new(Sub)
	mul := new(Mul)
	calc1 := CalcParser{exp1: add, exp2: pow}
	fmt.Println("3+(6^2)=", calc1.interperter(3, 6, 2))
	calc2 := CalcParser{exp1: mul, exp2: sub}
	fmt.Println("4*(5-2)=", calc2.interperter(4, 5, 2))
}

// 解释器
type Interpreter interface {
	Expression(a, b int) int
}
type CalcParser struct {
	exp1 Interpreter
	exp2 Interpreter
}
type Add struct {
}
type Sub struct {
}
type Mul struct {
}
type Pow struct {
}

func (p Add) Expression(a, b int) int {
	return a + b
}
func (p Sub) Expression(a, b int) int {
	return a - b
}
func (p Mul) Expression(a, b int) int {
	return a * b
}
func (p Pow) Expression(a, b int) int {
	res := 1
	for i := 0; i < b; i++ {
		res *= a
	}
	return res
}

// 解析成 exp1(a , exp2(b, c)) 括号计算的形式
func (calc CalcParser) interperter(a, b, c int) int {
	return calc.exp1.Expression(a, calc.exp2.Expression(b, c))
}
/*
3+(6^2)= 39
4*(5-2)= 12

迭代器模式

提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

优点:
1、支持以不同的方式遍历一个聚合对象。
2、迭代器简化了聚合类。
3、在同一个聚合上可以有多个遍历。
4、增加新的聚合类和迭代器类都很方便,无须修改原有代码。

缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,
类的个数成对增加,这在一定程度上增加了系统的复杂性。

迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

func main() {
	objects := objects{1, 2, 3, "张三", "李四", "王五", "赵六"}
	for iter := objects.Iterator(); iter.HasNext(); {
		fmt.Println(iter.Next())
	}
}

type objects []interface{}

type Iterator struct {
	objects []interface{}
	index   int
}

func (p objects) Iterator() *Iterator {
	return &Iterator{
		objects: p,
		index:   0,
	}
}
func (p *Iterator) HasNext() bool {
	return p.index < len(p.objects)
}
func (p *Iterator) Next() (object interface{}) {
	object = p.objects[p.index]
	p.index++
	return
}
/*
1
2
3
张三
李四
王五
赵六

中介者模式

用一个中介对象来封装一系列的对象交互,中介使各对象不需要显式地相互引用,耦合松散,而且可以独立地改变它们之间的交互。

优点:
1、降低了类的复杂度,将一对多转化成了一对一。
2、各个类之间的解耦。
3、符合迪米特原则。

缺点:中介者会庞大,变得复杂难以维护。

MVC架构,C控制器 就是 M模型 和 V视图 的中介。

func main() {
	chatRoom = NewChatRoom()
	user1 := User{name: "张三"}
	user2 := User{name: "李四"}
	user1.sendMessage("你好啊!李四")
	user2.sendMessage("你好,张三")
}

var chatRoom *ChatRoom

type User struct {
	name string
}
type ChatRoom struct {
}

func (receiver ChatRoom) showMessage(user User, message string) {
	fmt.Println(time.Now().Format("2006/01/02 15:04:05"), "[", user.name, "]:", message)
}
func (receiver User) sendMessage(message string) {
	chatRoom.showMessage(receiver, message)
}
func NewChatRoom() *ChatRoom {
	fmt.Println("-------- 私人聊天室搭建 ------")
	return new(ChatRoom)
}
/*
-------- 私人聊天室搭建 ------
2022/10/22 02:25:08 [ 张三 ]: 你好啊!李四
2022/10/22 02:25:08 [ 李四 ]: 你好,张三

备忘录模式

在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。

优点:
1、提供可以恢复状态的机制,使用户能够比较方便地回到某个历史的状态。
2、实现了信息的封装,使得用户不需要关心状态的保存细节。

缺点:消耗资源。如果类的成员变量过多,会占用比较大的资源,而且每一次保存都会消耗一定的内存。

为了符合迪米特原则,还要增加一个管理备忘录的类。 为了节约内存,可使用原型模式+备忘录模式。

撤回、存档、回滚

面向对象行为型模式(go语言)_第2张图片

func main() {
	editor = NewEditor()
	editor.Write("《花海-周杰伦》")
	editor.Write("不要你离开,距离隔不开。")
	editor.Write("思念变成海,在窗外进不来。")
	editor.Empty()
	editor.Undo()
}

var editor Editor

// 历史记录
type History struct {
	data string
}
func (t History) GetHistory() string {
	return t.data
}

// 文档
type Doc struct {
	body string
}
func (t *Doc) SetBody(body string) {
	t.body = body
}
func (t *Doc) GetBody() string {
	return t.body
}
func (t *Doc) CreateHistory() History { // 备份
	return History{data: t.body}
}
func (t *Doc) RecoverHistory(h History) { // 回滚
	t.body = h.data
}

// 文本编辑器
type Editor struct {
	doc     Doc
	history []History
	index   int
}
func NewEditor() Editor {
	fmt.Println("---------- 新建文本 --------")
	return Editor{doc: Doc{""}, index: -1}
}
func (t *Editor) Show() {
	fmt.Println(t.doc.body)
}
func (t *Editor) BackUp() {
	t.history = append(t.history, t.doc.CreateHistory())
	t.index++
}
func (t *Editor) Write(txt string) {
	fmt.Print("写入->")
	t.doc.SetBody(t.doc.GetBody() + txt)
	t.BackUp()
	t.Show()
}
func (t *Editor) Empty() {
	fmt.Print("清空->[ 空 ]")
	t.doc.SetBody("")
	t.BackUp()
	t.Show()
}
func (t *Editor) Undo() {
	if t.index <= 0 {
		fmt.Print("历史记录->[ 空 ]")
		return
	}
	fmt.Print("回滚->")
	t.index--
	t.doc.RecoverHistory(t.history[t.index])
	t.Show()
}
/*
---------- 新建文本 --------
写入->《花海-周杰伦》
写入->《花海-周杰伦》不要你离开,距离隔不开。
写入->《花海-周杰伦》不要你离开,距离隔不开。思念变成海,在窗外进不来。
清空->[ 空 ]
回滚->《花海-周杰伦》不要你离开,距离隔不开。思念变成海,在窗外进不来。

观察者模式

对象间的一种一对多的依赖关系,当一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知并被自动更新。

优点:
1、观察者和被观察者是抽象耦合的。
2、建立一套触发机制。
3、符合开闭原则,无需修改被观察者代码就可以引入新的观察者。

缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
4、通知的顺序是随机的。

被观察者对象在发送广播通知时,不需要指定具体的观察者对象,可自己决定是否订阅被观察者对象的通知。
被观察者至少有三个方法:添加观察者、移除观察者、通知观察者。观察者至少有一个方法:更新方法,做出相应的处理。

避免循环引用,如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

推模型

被观察者对象向观察者对象推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。
一般在这种模型的实现中会将被观察者对象中的全部或部分信息通过作为观察者的更新方法的参数传递给观察者。

拉模型

被观察者对象在通知观察者时,只传递少量信息。如果观察者需要更详细的信息,由观察者主动到被观察者对象中获取,相当于观察者从被观察者对象中拉取数据。

面向对象行为型模式(go语言)_第3张图片

func main() {
	good1 := NewGoods("苹果电脑")
	user1 := NewPeople("张三")
	user2 := NewPeople("李四")

	good1.Register(user1)
	good1.Register(user2)
	good1.updateState()
	fmt.Println("-------------------")
	good1.Remove(user1)
	good1.updateState()
}

// 被观察者
type BeObserved interface {
	Register(people Observe) // 注册
	Remove(people Observe)   // 移除
	Notify()                 // 广播
}
// 商品
type Goods struct {
	Name    string
	peoples []Observe
}

func NewGoods(name string) *Goods {
	return &Goods{
		Name: name,
	}
}
func (g *Goods) updateState() {
	fmt.Printf("货物 %s 现在是打折了!!!\n", g.Name)
	g.Notify()
}
func (g *Goods) Notify() {
	for _, people := range g.peoples {
		people.Update(g.Name)
	}
}
func (g *Goods) Register(people Observe) {
	g.peoples = append(g.peoples, people)
	fmt.Printf("[%s] 成功开通 VIP!!!\n", people.GetName())
}
func (g *Goods) Remove(people Observe) {
	g.peoples = g.removePeople(g.peoples, people)
	fmt.Printf("[%s] 成功注销 VIP!!!\n", people.GetName())
}
func (g *Goods) removePeople(peoples []Observe, people Observe) []Observe {
	index, err := indexOf(peoples, people.GetName())
	if err != nil {
		fmt.Println(err)
		return peoples
	}
	peoples = append(peoples[:index], peoples[index+1:]...)
	return peoples
}

// 观察者
type Observe interface {
	Update(gName string)
	GetName() string
}
type People struct {
	name string
}

func NewPeople(name string) *People {
	return &People{
		name: name,
	}
}
func (p *People) Update(gName string) {
	fmt.Printf("[%s]>>VIP已收到信息:%s 打折了。。。\n", p.name, gName)
}
func (p *People) GetName() string {
	return p.name
}

func indexOf(peoples []Observe, name string) (int, error) {
	for index, item := range peoples {
		if item.GetName() == name {
			return index, nil
		}
	}
	return 0, errors.New("该用户不存在!!!")
}
/*
[张三] 成功开通 VIP!!!
[李四] 成功开通 VIP!!!
货物 苹果电脑 现在是打折了!!!
[张三]>>VIP已收到信息:苹果电脑 打折了。。。
[李四]>>VIP已收到信息:苹果电脑 打折了。。。
-------------------
[张三] 成功注销 VIP!!!
货物 苹果电脑 现在是打折了!!!
[李四]>>VIP已收到信息:苹果电脑 打折了。。。

状态模式

允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类,各种状态的对象和一个行为随着状态对象改变而改变。

优点:
1、封装了转换规则。
2、枚举可能的状态。
3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的 if-else 语句块。
5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点:
1、增加系统类和对象的个数。
2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
3、对开闭原则的支持并不好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

Context 环境类、State 抽象状态类、Concrete State 具体状态类

面向对象行为型模式(go语言)_第4张图片

func main() {
	u1 := NewUser("张三", -10)
	u2 := NewUser("李四", 40)
	u3 := NewUser("王五", 80)

	fmt.Printf("%s >>", u1.Name)
	u1.View()
	fmt.Printf("%s >>", u2.Name)
	u2.Post()
	fmt.Printf("%s >>", u3.Name)
	u3.Comment()
}

type ActionState interface {
	View()    // 查看
	Comment() // 评论
	Post()    // 发帖
}
type User struct {
	Name        string
	State       ActionState // 状态
	HealthPoint int         // 信誉分
}
type ClosedState struct { // 封号
}
type LimitedState struct { // 受限
}
type NormalState struct { // 正常
}

func NewUser(name string, health int) *User {
	user := &User{
		Name:        name,
		HealthPoint: health,
	}
	user.switchState()
	return user
}
func (user *User) SetHealth(value int) {
	user.HealthPoint = value
	user.switchState()
}
func (user *User) switchState() {
	if user.HealthPoint <= 0 {
		user.State = new(ClosedState)
	} else if user.HealthPoint > 0 && user.HealthPoint <= 60 {
		user.State = new(LimitedState)
	} else if user.HealthPoint > 60 {
		user.State = new(NormalState)
	}
}
func (user *User) View() {
	user.State.View()
}
func (user *User) Comment() {
	user.State.Comment()
}
func (user *User) Post() {
	user.State.Post()
}

func (c *ClosedState) View() {
	fmt.Print("[+]封号用户:账号被封,封号处理,无法查看\n")
}
func (c *ClosedState) Comment() {
	fmt.Print("[+]封号用户:信誉分过低,封号处理,不能评论\n")
}
func (c *ClosedState) Post() {
	fmt.Print("[+]封号用户:信誉分过低,封号处理,不能发帖\n")
}

func (r *LimitedState) View() {
	fmt.Print("[+]受限用户:正常查看\n")
}
func (r *LimitedState) Comment() {
	fmt.Print("[+]受限用户:正常评论\n")
}
func (r *LimitedState) Post() {
	fmt.Print("[+]受限用户:信誉分低于60,账号受限,不能发帖\n")
}

func (n *NormalState) View() {
	fmt.Print("[+]正常用户:可以查看\n")
}
func (n *NormalState) Comment() {
	fmt.Print("[+]正常用户:可以评论\n")
}
func (n *NormalState) Post() {
	fmt.Print("[+]正常用户:可以发帖\n")
}
/*
张三 >>[+]封号用户:账号被封,封号处理,无法查看
李四 >>[+]受限用户:信誉分低于60,账号受限,不能发帖
王五 >>[+]正常用户:可以评论

策略模式

定义一系列的算法,把他们一个个封装起来,并且可以相互替换,使用巨大的 if-else 复杂且难以维护。

优点:
1、算法可以自由切换。
2、避免使用多重条件判断。
3、扩展性良好。

缺点:
1、策略类会增多。
2、所有策略类都需要对外暴露。

一个系统里面有许多类,他们之间的区别仅在于行为,可以动态的让一个对象在许多行为中选择一种行为。
比如支付系统中的不同支付方式,可以是微信支付的 api,支付宝支付的 api,网银的api,if-else 虽然也可以满足需求,但是当业务扩展或改变时需要修改源代码,这种就不符合开闭原则。

面向对象行为型模式(go语言)_第5张图片

func main() {
	Service = NewPaymentService()
	Service.Payment(new(Wechat), 10)
	Service.Payment(new(Alipay), 100)
}

var Service *PaymentService

type PaymentStrategy interface {
	Payment(cost int)
}
type Wechat struct {
}
type Alipay struct {
}

func (receiver Wechat) Payment(cost int) {
	fmt.Println("微信支付:", cost)
}
func (receiver Alipay) Payment(cost int) {
	fmt.Println("支付宝支付:", cost)
}

type PaymentService struct {
}
func (receiver PaymentService) Payment(strategy PaymentStrategy, cost int) {
	strategy.Payment(cost)
}
func NewPaymentService() *PaymentService {
	return new(PaymentService)
}
/*
微信支付: 10
支付宝支付: 100

模板模式

定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。

比如说一些通用方法,子类不需要重写方法,就像做饭,洗菜、开火都是一样的先后顺序,子类不同的是菜不同和子类一些自己的烹饪步骤。

优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。

缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

在抽象父类实现通用方法,其他步骤延迟到子类实现,封装不变的部分,扩展可变的部分。

func main() {
	var g Game
	g = new(lol)
	Play(g)
  
	g = new(cf)
	Play(g)
}

type Game interface {
	init()
	start()
	end()
}

// 抽象父类完成通用的方法
type game struct {
}
func (receiver game) init() {
	fmt.Println("---- 游戏初始化完成 ----")
}
func (receiver game) start() {
	fmt.Println("---- 游戏开始 ----")
}
func (receiver game) end() {
	fmt.Println("---- 游戏结束 ----")
}

// 封装不变的部分
func Play(g Game) {
	g.init()
	g.start()
	fmt.Println(g)
	g.end()
}

// 子类无需重写通用方法,继承抽象父类即可
type lol struct {
	game
}
type cf struct {
	game
}
func (receiver lol) String() string {
	return fmt.Sprint("英雄联盟")
}
func (receiver cf) String() string {
	return fmt.Sprint("穿越火线")
}
/*
---- 游戏初始化完成 ----
---- 游戏开始 ----
英雄联盟
---- 游戏结束 ----

---- 游戏初始化完成 ----
---- 游戏开始 ----
穿越火线
---- 游戏结束 ----

访问者模式

执行算法可以随着访问者改变而改变,需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。

优点:
1、符合单一职责原则。
2、优秀的扩展性、灵活性。

缺点:
1、具体元素对访问者公布细节,违反了迪米特原则。
2、具体元素变更比较困难。
3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。

类似拦截器、过滤器这种。

在被访问的类里面加一个对外提供接待访问者的接口,将自身引用传入访问者。

面向对象行为型模式(go语言)_第6张图片

func main() {
	visitor := ComputerVisitor{}

	computer := NewComputer()
	computer.accept(visitor)

	monitor := NewMonitor()
	monitor.accept(visitor)

	keyBoard := NewKeyboard()
	keyBoard.accept(visitor)

	mouse := NewMouse()
	mouse.accept(visitor)
}

type ComputerPart interface { // 访问入口
	accept(visitor ComputerVisitor)
}

type Monitor struct {
}
type Mouse struct {
}
type KeyBoard struct {
}
type Computer struct {
	parts []ComputerPart
}

func (receiver Monitor) accept(visitor ComputerVisitor) {
	visitor.visitMonitor(receiver)
}
func (receiver Mouse) accept(visitor ComputerVisitor) {
	visitor.visitMouse(receiver)
}
func (receiver KeyBoard) accept(visitor ComputerVisitor) {
	visitor.visitKeyBoard(receiver)
}
func (receiver Computer) accept(visitor ComputerVisitor) {
	visitor.visitComputer(receiver)
	fmt.Println("--- 访问电脑的其他组件 ---")
	for i := 0; i < len(receiver.parts); i++ {
		receiver.parts[i].accept(visitor)
	}
	fmt.Println("-----------------------")
}
func (receiver Mouse) Play() {
	fmt.Println("Mouse 成功处理访问者请求")
}
func (receiver KeyBoard) Play() {
	fmt.Println("KeyBoard 成功处理访问者请求")
}
func (receiver Monitor) Play() {
	fmt.Println("Monitor 成功处理访问者请求")
}
func (receiver Computer) Play() {
	fmt.Println("Computer 成功处理访问者请求")
}
func NewComputer() *Computer {
	return &Computer{
		parts: []ComputerPart{new(Monitor), new(Mouse), new(KeyBoard)},
	}
}
func NewMouse() *Mouse {
	return new(Mouse)
}
func NewMonitor() *Monitor {
	return new(Monitor)
}
func NewKeyboard() *KeyBoard {
	return new(KeyBoard)
}

type ComputerVisitor struct { // 访问者
}

func (receiver ComputerVisitor) visitMouse(mouse Mouse) {
	fmt.Println("访问 mouse")
	mouse.Play()
}
func (receiver ComputerVisitor) visitMonitor(monitor Monitor) {
	fmt.Println("访问 monitor")
	monitor.Play()
}
func (receiver ComputerVisitor) visitKeyBoard(keyboard KeyBoard) {
	fmt.Println("访问 keyboard")
	keyboard.Play()
}
func (receiver ComputerVisitor) visitComputer(computer Computer) {
	fmt.Println("访问 computer")
	computer.Play()
}
/*
访问 computer
Computer 成功处理访问者请求
--- 访问电脑的所有组件 ---
访问 monitor
Monitor 成功处理访问者请求
访问 mouse
Mouse 成功处理访问者请求
访问 keyboard
KeyBoard 成功处理访问者请求
-----------------------

访问 monitor
Monitor 成功处理访问者请求
访问 keyboard
KeyBoard 成功处理访问者请求
访问 mouse
Mouse 成功处理访问者请求

你可能感兴趣的:(设计模式学习(go语言),1024程序员节,设计模式)