行为型模式
行为型模式
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
模板方法模式
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
- 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
- 基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。
具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。
很典型的一个例子,go 标准库中排序sort
包下 的Interface
接口,其内部定义了三个基本方法和几个模板方法,倘若想要自定义数据结构排序,就必须要实现这三个方法,模板方法内会将基本方法的返回值当作排序的依据。
例:北方人都喜欢吃面,而煮面的步骤都是相同的,只是其中的细节和顺序不同,分为,烧水,下面,挑面,放调料。
type CookNoodles interface {
Water()
Noodles()
TakeNoodles()
Condiment()
Cook()
}
type template struct {
CookNoodles
}
func (t template) Cook() {
t.Water()
t.Noodles()
t.TakeNoodles()
t.Condiment()
fmt.Println("面煮完了")
}
type BeefNoodles struct {
*template
}
func (b BeefNoodles) Water() {
fmt.Println("烧水5分钟")
}
func (b BeefNoodles) Noodles() {
fmt.Println("加入刀削面")
}
func (b BeefNoodles) TakeNoodles() {
fmt.Println("用筷子挑面")
}
func (b BeefNoodles) Condiment() {
fmt.Println("加入牛肉和调料")
}
func NewCookNoodles() CookNoodles {
tem := template{}
noodles := BeefNoodles{template: &tem}
// 父类持有子类的引用
tem.CookNoodles = noodles
return noodles
}
func TestNoodles(t *testing.T) {
noodles := NewCookNoodles()
noodles.Cook()
}
go语言是不提供继承机制的,这里是用匿名组合和手动让父类持有子类引用来模拟的,官方的解决办法是在sort
包下,直接将模板方法作为了私有的函数,而不是成员方法,个人认为官方的解决办法会更好一些。
观察者模式
又被称为发布-订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
- Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
- Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
- ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
例:一个公众号有很多个订阅用户,公众号更新时会自动通知用户,用户收到通知便会做出相应的行为。
// 抽象主题
type Subject interface {
Attach(Observer)
Detach(Observer)
Notify(string)
}
// 具体主题
type WeiXinOffical struct {
observers []Observer
}
// 构造方法
func NewWeiXinOffical() *WeiXinOffical {
return &WeiXinOffical{observers: make([]Observer, 0)}
}
func (w *WeiXinOffical) Attach(observer Observer) {
w.observers = append(w.observers, observer)
}
func (w *WeiXinOffical) Detach(observer Observer) {
for i, o := range w.observers {
if o == observer {
w.observers = append(w.observers[:i], w.observers[i+1:]...)
}
}
}
func (w *WeiXinOffical) Notify(s string) {
fmt.Println(s)
for _, observer := range w.observers {
observer.Update("公众号更新了")
}
}
// 抽象观察者
type Observer interface {
Update(string)
}
// 具体观察者
type WeiXinUser struct {
}
func (w WeiXinUser) Update(s string) {
fmt.Println("收到更新了,赶紧去点赞")
}
func TestObserver(t *testing.T) {
offical := NewWeiXinOffical()
offical.Attach(WeiXinUser{})
offical.Attach(WeiXinUser{})
offical.Attach(WeiXinUser{})
offical.Notify("震惊,Go语言将推出新泛型机制!")
}
备忘录模式
又叫快照模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
- 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
- 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
- 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
提示
备忘录有两个等效的接口:
窄接口:管理者(Caretaker)对象(和其他发起人对象之外的任何对象)看到的是备忘录的窄接口(narror Interface),这个窄接口只允许他把备忘录对象传给其他的对象。
宽接口:与管理者看到的窄接口相反,发起人对象可以看到一个宽接口(wide Interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。
白箱模式
// 存档对象 白箱模式下存档对象是对外暴露的
type GameMemento struct {
hp, ak, def int
}
// 玩家对象
type GamePlayer struct {
*GameMemento
}
// 构造方法
func NewGamePlayer() *GamePlayer {
return &GamePlayer{GameMemento: &GameMemento{
hp: 100,
ak: 100,
def: 100,
}}
}
func (g *GamePlayer) Fight() {
g.hp -= 1
g.ak -= 1
g.def -= 1
}
func (g *GamePlayer) Save() *GameMemento {
return &GameMemento{
hp: g.hp,
ak: g.ak,
def: g.def,
}
}
func (g *GamePlayer) Load(memento *GameMemento) {
g.hp = memento.hp
g.ak = memento.ak
g.def = memento.def
}
// 存档保存对象
type MementoTaker struct {
memento *GameMemento
}
// 保存对象知道保存的是玩家的数据存档
func (m *MementoTaker) Set(memento *GameMemento) {
m.memento = memento
}
func (m *MementoTaker) Get() *GameMemento {
return m.memento
}
func TestMemento(t *testing.T) {
player := NewGamePlayer()
taker := MementoTaker{}
// 战斗前先存档
taker.Set(player.Save())
// 战斗
player.Fight()
player.Load(taker.Get())
}
黑箱模式
// 备忘录对象
type Memento interface {
}
// 存档对象 黑箱模式 存档数据不对外公开
type gameMemento struct {
hp, ak, def int
}
// 玩家对象
type GamePlayer struct {
*gameMemento
}
// 构造方法
func NewGamePlayer() *GamePlayer {
return &GamePlayer{gameMemento: &gameMemento{
hp: 100,
ak: 100,
def: 100,
}}
}
func (g *GamePlayer) Fight() {
g.hp -= 1
g.ak -= 1
g.def -= 1
}
func (g *GamePlayer) Save() Memento {
return gameMemento{
hp: g.hp,
ak: g.ak,
def: g.def,
}
}
func (g *GamePlayer) Load(memento gameMemento) {
g.hp = memento.hp
g.ak = memento.ak
g.def = memento.def
}
// 存档保存对象
type MementoTaker struct {
memento Memento
}
// 只知道是一个备忘录对象,不知道保存的是个什么东西,仅仅只负责存储
func (m *MementoTaker) Set(memento Memento) {
m.memento = memento
}
func (m *MementoTaker) Get() Memento {
return m.memento
}
func TestMemento(t *testing.T) {
player := NewGamePlayer()
taker := MementoTaker{}
// 战斗前先存档
taker.Set(player.Save())
// 战斗
player.Fight()
player.Load(taker.Get().(gameMemento))
}
优点:
提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录 中,并由管理者进行管理,这符合单一职责原则。
缺点:
资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
责任链模式
又名职责链模式,为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
- 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
- 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
- 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
提示
Gin框架内的中间件就是责任链模式的一个应用。
例子:现需要开发一个请假流程控制系统。请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部门经理同意;请求3天到7天还需要总经理同意才行。
// 处理者
type Handler interface {
Handle(Handlee) bool
}
// 小组长
type GroupManager struct {
}
func (g GroupManager) Handle(handlee Handlee) bool {
if handlee.Info() <= 1 {
return true
}
return false
}
// 部门经理
type Manager struct {
}
func (m Manager) Handle(handlee Handlee) bool {
if handlee.Info() <= 3 {
return true
}
return false
}
type GeneralManager struct {
}
func (g GeneralManager) Handle(handlee Handlee) bool {
if handlee.Info() <= 7 {
return true
}
return false
}
// 被处理者不一定要是接口,具体的类也可以,被处理者的设计可以很灵活
type Handlee interface {
Info() int
}
// 请假条
type LeaveReq struct {
days int
}
func (l LeaveReq) Info() int {
return l.days
}
// 调用链
type HanlderChain struct {
chain []Handler
}
func NewHanlderChain() *HanlderChain {
return &HanlderChain{chain: make([]Handler, 0)}
}
// 将调用链里的处理器一个个调用
func (h *HanlderChain) Next(handlee Handlee) bool {
for _, handler := range h.chain {
if handler.Handle(handlee) {
return true
}
}
return false
}
// 添加处理器,删除也可以有,这类省略了
func (h *HanlderChain) addHandler(handler Handler) {
h.chain = append(h.chain, handler)
}
func TestResponse(t *testing.T) {
handlerChain := NewHanlderChain()
handlerChain.addHandler(GroupManager{})
handlerChain.addHandler(Manager{})
handlerChain.addHandler(GeneralManager{})
fmt.Println(handlerChain.Next(LeaveReq{3}))
}
其实代码也还可以再简化,本质上调用链传的只是一个个方法,可以不需要Handler
接口,将其换为类型为Func(Handlee) bool
的类型会更好一些。
优点
降低了对象之间的耦合度该模式降低了请求发送者和接收者的耦合度。
增强了系统的可扩展性可以根据需要增加新的请求处理类,满足开闭原则。
增强了给对象指派职责的灵活性当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。
责任链简化了对象之间的连接
缺点
不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理, 该请求可能一直传到链的末端都得不到处理。 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
策略模式
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用 算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分 割开来,并委派给不同的对象对这些算法进行管理。
- 抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口实现。此角色给出所有 的具体策略类所需的接口。
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
一个商店同时支持微信支付和支付宝支付,只要传入对应的支付方式就可以支付。
// 抽象策略类
type Strategy interface {
pay(int) string
}
// 具体策略类
type WeiXinPay struct {
}
func (w WeiXinPay) pay(i int) string {
return fmt.Sprintf("微信支付: %d 元", i)
}
// 具体策略类
type AliPay struct {
}
func (a AliPay) pay(i int) string {
return fmt.Sprintf("支付宝支付: %d 元", i)
}
// 环境上下文类,持有策略的引用
type Shop struct {
strategy Strategy
}
func (s *Shop) SetStrategy(strategy Strategy) {
s.strategy = strategy
}
func (s *Shop) Show(num int) {
fmt.Println(s.strategy.pay(num))
}
func TestStrategy(t *testing.T) {
shop := Shop{}
shop.SetStrategy(WeiXinPay{})
shop.Show(10)
shop.SetStrategy(AliPay{})
shop.Show(20)
}
- 策略类之间可以自由切换,由于策略类都实现同一个接口,所以使它们之间可以自由切换。
- 易于扩展,增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合“开闭原则“
- 避免使用多重条件选择语句(if else),充分体现面向对象设计思想。
命令模式
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象 进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。
- 抽象命令类(Command)角色: 定义命令的接口,声明执行的方法。
- 具体命令(Concrete Command)角色:具体的命令,实现命令接口;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
- 实现者/接收者(Receiver)角色: 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
- 调用者/请求者(Invoker)角色: 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用 命令对象的入口。
// 抽象命令
type Command interface {
Execute()
}
// 具体命令
type StartCommand struct {
mb *MotherBoard
}
func NewStartCommand(mb *MotherBoard) *StartCommand {
return &StartCommand{
mb: mb,
}
}
func (c *StartCommand) Execute() {
c.mb.Start()
}
// 具体命令
type RebootCommand struct {
mb *MotherBoard
}
func NewRebootCommand(mb *MotherBoard) *RebootCommand {
return &RebootCommand{
mb: mb,
}
}
func (c *RebootCommand) Execute() {
c.mb.Reboot()
}
type MotherBoard struct{}
func (*MotherBoard) Start() {
fmt.Print("system starting\n")
}
func (*MotherBoard) Reboot() {
fmt.Print("system rebooting\n")
}
// 调用者
type Box struct {
button1 Command
button2 Command
}
func NewBox(button1, button2 Command) *Box {
return &Box{
button1: button1,
button2: button2,
}
}
func (b *Box) PressButton1() {
b.button1.Execute()
}
func (b *Box) PressButton2() {
b.button2.Execute()
}
func ExampleCommand() {
mb := &MotherBoard{}
startCommand := NewStartCommand(mb)
rebootCommand := NewRebootCommand(mb)
box1 := NewBox(startCommand, rebootCommand)
box1.PressButton1()
box1.PressButton2()
box2 := NewBox(rebootCommand, startCommand)
box2.PressButton1()
box2.PressButton2()
// Output:
// system starting
// system rebooting
// system rebooting
// system starting
}
迭代器模式
提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示
- 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
- 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
- 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、 next() 等方法。
- 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对 象的遍历,记录遍历的当前位置。
// 抽象聚合对象
type Aggregate interface {
Iterator() Iterator
}
// 抽象迭代器
type Iterator interface {
First()
IsDone() bool
Next() interface{}
}
// 具体聚合对象
type Numbers struct {
start, end int
}
func NewNumbers(start, end int) *Numbers {
return &Numbers{
start: start,
end: end,
}
}
func (n *Numbers) Iterator() Iterator {
return &NumbersIterator{
numbers: n,
next: n.start,
}
}
// 具体迭代器
type NumbersIterator struct {
numbers *Numbers
next int
}
func (i *NumbersIterator) First() {
i.next = i.numbers.start
}
func (i *NumbersIterator) IsDone() bool {
return i.next > i.numbers.end
}
func (i *NumbersIterator) Next() interface{} {
if !i.IsDone() {
next := i.next
i.next++
return next
}
return nil
}
func IteratorPrint(i Iterator) {
for i.First(); !i.IsDone(); {
c := i.Next()
fmt.Printf("%#v\n", c)
}
}
func ExampleIterator() {
var aggregate Aggregate
aggregate = NewNumbers(1, 10)
IteratorPrint(aggregate.Iterator())
// Output:
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
// 10
}
状态模式
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变 时改变其行为。
- 环境(Context)角色:也称为上下文,它定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
- 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
- 具体状态(Concrete State)角色:实现抽象状态所对应的行为。
type Week interface {
Today()
Next(*DayContext)
}
type DayContext struct {
today Week
}
func NewDayContext() *DayContext {
return &DayContext{
today: &Sunday{},
}
}
func (d *DayContext) Today() {
d.today.Today()
}
func (d *DayContext) Next() {
d.today.Next(d)
}
type Sunday struct{}
func (*Sunday) Today() {
fmt.Printf("Sunday\n")
}
func (*Sunday) Next(ctx *DayContext) {
ctx.today = &Monday{}
}
type Monday struct{}
func (*Monday) Today() {
fmt.Printf("Monday\n")
}
func (*Monday) Next(ctx *DayContext) {
ctx.today = &Tuesday{}
}
type Tuesday struct{}
func (*Tuesday) Today() {
fmt.Printf("Tuesday\n")
}
func (*Tuesday) Next(ctx *DayContext) {
ctx.today = &Wednesday{}
}
type Wednesday struct{}
func (*Wednesday) Today() {
fmt.Printf("Wednesday\n")
}
func (*Wednesday) Next(ctx *DayContext) {
ctx.today = &Thursday{}
}
type Thursday struct{}
func (*Thursday) Today() {
fmt.Printf("Thursday\n")
}
func (*Thursday) Next(ctx *DayContext) {
ctx.today = &Friday{}
}
type Friday struct{}
func (*Friday) Today() {
fmt.Printf("Friday\n")
}
func (*Friday) Next(ctx *DayContext) {
ctx.today = &Saturday{}
}
type Saturday struct{}
func (*Saturday) Today() {
fmt.Printf("Saturday\n")
}
func (*Saturday) Next(ctx *DayContext) {
ctx.today = &Sunday{}
}
func ExampleWeek() {
ctx := NewDayContext()
todayAndNext := func() {
ctx.Today()
ctx.Next()
}
for i := 0; i < 8; i++ {
todayAndNext()
}
// Output:
// Sunday
// Monday
// Tuesday
// Wednesday
// Thursday
// Friday
// Saturday
// Sunday
}
解释器模式
解释器模式定义一套语言文法,并设计该语言解释器,使用户能使用特定文法控制解释器行为。解释器模式的意义在于,它分离多种复杂功能的实现,每个功能只需关注自身的解释。对于调用者不用关心内部的解释器的工作,只需要用简单的方式组合命令就可以。
type Node interface {
Interpret() int
}
type ValNode struct {
val int
}
func (n *ValNode) Interpret() int {
return n.val
}
type AddNode struct {
left, right Node
}
func (n *AddNode) Interpret() int {
return n.left.Interpret() + n.right.Interpret()
}
type MinNode struct {
left, right Node
}
func (n *MinNode) Interpret() int {
return n.left.Interpret() - n.right.Interpret()
}
type Parser struct {
exp []string
index int
prev Node
}
func (p *Parser) Parse(exp string) {
p.exp = strings.Split(exp, " ")
for {
if p.index >= len(p.exp) {
return
}
switch p.exp[p.index] {
case "+":
p.prev = p.newAddNode()
case "-":
p.prev = p.newMinNode()
default:
p.prev = p.newValNode()
}
}
}
func (p *Parser) newAddNode() Node {
p.index++
return &AddNode{
left: p.prev,
right: p.newValNode(),
}
}
func (p *Parser) newMinNode() Node {
p.index++
return &MinNode{
left: p.prev,
right: p.newValNode(),
}
}
func (p *Parser) newValNode() Node {
v, _ := strconv.Atoi(p.exp[p.index])
p.index++
return &ValNode{
val: v,
}
}
func (p *Parser) Result() Node {
return p.prev
}
func TestInterpreter(t *testing.T) {
p := &Parser{}
p.Parse("1 + 2 + 3 - 4 + 5 - 6")
res := p.Result().Interpret()
expect := 1
if res != expect {
t.Fatalf("expect %d got %d", expect, res)
}
}
访问者模式
封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。
// 抽象元素
type Customer interface {
Accept(Visitor)
}
// 抽象访问者
type Visitor interface {
Visit(Customer)
}
type EnterpriseCustomer struct {
name string
}
type CustomerCol struct {
customers []Customer
}
func (c *CustomerCol) Add(customer Customer) {
c.customers = append(c.customers, customer)
}
func (c *CustomerCol) Accept(visitor Visitor) {
for _, customer := range c.customers {
customer.Accept(visitor)
}
}
func NewEnterpriseCustomer(name string) *EnterpriseCustomer {
return &EnterpriseCustomer{
name: name,
}
}
func (c *EnterpriseCustomer) Accept(visitor Visitor) {
visitor.Visit(c)
}
type IndividualCustomer struct {
name string
}
func NewIndividualCustomer(name string) *IndividualCustomer {
return &IndividualCustomer{
name: name,
}
}
func (c *IndividualCustomer) Accept(visitor Visitor) {
visitor.Visit(c)
}
type ServiceRequestVisitor struct{}
func (*ServiceRequestVisitor) Visit(customer Customer) {
switch c := customer.(type) {
case *EnterpriseCustomer:
fmt.Printf("serving enterprise customer %s\n", c.name)
case *IndividualCustomer:
fmt.Printf("serving individual customer %s\n", c.name)
}
}
// only for enterprise
type AnalysisVisitor struct{}
func (*AnalysisVisitor) Visit(customer Customer) {
switch c := customer.(type) {
case *EnterpriseCustomer:
fmt.Printf("analysis enterprise customer %s\n", c.name)
}
}
func ExampleRequestVisitor() {
c := &CustomerCol{}
c.Add(NewEnterpriseCustomer("A company"))
c.Add(NewEnterpriseCustomer("B company"))
c.Add(NewIndividualCustomer("bob"))
c.Accept(&ServiceRequestVisitor{})
// Output:
// serving enterprise customer A company
// serving enterprise customer B company
// serving individual customer bob
}
func ExampleAnalysis() {
c := &CustomerCol{}
c.Add(NewEnterpriseCustomer("A company"))
c.Add(NewIndividualCustomer("bob"))
c.Add(NewEnterpriseCustomer("B company"))
c.Accept(&AnalysisVisitor{})
// Output:
// analysis enterprise customer A company
// analysis enterprise customer B company
}
中介者模式
又叫调停模式,定义一个中介角色来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以 独立地改变它们之间的交互。
- 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
- 具体中介者(ConcreteMediator)角色:实现中介者接口,定义一个 List 来管理同事对 象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
- 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽 象方法,实现所有相互影响的同事类的公共功能。
- 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交 互时,由中介者对象负责后续的交互。
type CDDriver struct {
Data string
}
func (c *CDDriver) ReadData() {
c.Data = "music,image"
fmt.Printf("CDDriver: reading data %s\n", c.Data)
GetMediatorInstance().changed(c)
}
type CPU struct {
Video string
Sound string
}
func (c *CPU) Process(data string) {
sp := strings.Split(data, ",")
c.Sound = sp[0]
c.Video = sp[1]
fmt.Printf("CPU: split data with Sound %s, Video %s\n", c.Sound, c.Video)
GetMediatorInstance().changed(c)
}
type VideoCard struct {
Data string
}
func (v *VideoCard) Display(data string) {
v.Data = data
fmt.Printf("VideoCard: display %s\n", v.Data)
GetMediatorInstance().changed(v)
}
type SoundCard struct {
Data string
}
func (s *SoundCard) Play(data string) {
s.Data = data
fmt.Printf("SoundCard: play %s\n", s.Data)
GetMediatorInstance().changed(s)
}
type Mediator struct {
CD *CDDriver
CPU *CPU
Video *VideoCard
Sound *SoundCard
}
var mediator *Mediator
func GetMediatorInstance() *Mediator {
if mediator == nil {
mediator = &Mediator{}
}
return mediator
}
func (m *Mediator) changed(i interface{}) {
switch inst := i.(type) {
case *CDDriver:
m.CPU.Process(inst.Data)
case *CPU:
m.Sound.Play(inst.Sound)
m.Video.Display(inst.Video)
}
}
func TestMediator(t *testing.T) {
mediator := GetMediatorInstance()
mediator.CD = &CDDriver{}
mediator.CPU = &CPU{}
mediator.Video = &VideoCard{}
mediator.Sound = &SoundCard{}
//Tiggle
mediator.CD.ReadData()
if mediator.CD.Data != "music,image" {
t.Fatalf("CD unexpect data %s", mediator.CD.Data)
}
if mediator.CPU.Sound != "music" {
t.Fatalf("CPU unexpect sound data %s", mediator.CPU.Sound)
}
if mediator.CPU.Video != "image" {
t.Fatalf("CPU unexpect video data %s", mediator.CPU.Video)
}
if mediator.Video.Data != "image" {
t.Fatalf("VidoeCard unexpect data %s", mediator.Video.Data)
}
if mediator.Sound.Data != "music" {
t.Fatalf("SoundCard unexpect data %s", mediator.Sound.Data)
}
}