diff --git a/README.md b/README.md index 7d9f5f7..5d1102e 100644 --- a/README.md +++ b/README.md @@ -25,27 +25,27 @@ A curated collection of idiomatic design & application patterns for Go language. | Pattern | Description | Status | |:-------:|:----------- |:------:| -| [Bridge](/structural/bridge.md) | Decouples an interface from its implementation so that the two can vary independently | ✘ | -| [Composite](/structural/composite.md) | Encapsulates and provides access to a number of different objects | ✘ | +| [Bridge](/structural/bridge/main.go) | Decouples an interface from its implementation so that the two can vary independently | ✔ | +| [Composite](/structural/composite/main.go) | Encapsulates and provides access to a number of different objects | ✔ | | [Decorator](/structural/decorator.md) | Adds behavior to an object, statically or dynamically | ✔ | -| [Facade](/structural/facade.md) | Uses one type as an API to a number of others | ✘ | -| [Flyweight](/structural/flyweight.md) | Reuses existing instances of objects with similar/identical state to minimize resource usage | ✘ | +| [Facade](/structural/facade/main.go) | Uses one type as an API to a number of others | ✔ | +| [Flyweight](/structural/flyweight/main.go) | Reuses existing instances of objects with similar/identical state to minimize resource usage | ✔ | | [Proxy](/structural/proxy.md) | Provides a surrogate for an object to control it's actions | ✔ | ## Behavioral Patterns | Pattern | Description | Status | |:-------:|:----------- |:------:| -| [Chain of Responsibility](/behavioral/chain_of_responsibility.md) | Avoids coupling a sender to receiver by giving more than object a chance to handle the request | ✘ | -| [Command](/behavioral/command.md) | Bundles a command and arguments to call later | ✘ | -| [Mediator](/behavioral/mediator.md) | Connects objects and acts as a proxy | ✘ | -| [Memento](/behavioral/memento.md) | Generate an opaque token that can be used to go back to a previous state | ✘ | +| [Chain of Responsibility](/behavioral/chain_of_responsibility/main.go) | Avoids coupling a sender to receiver by giving more than object a chance to handle the request | ✔ | +| [Command](/behavioral/command/main.go) | Bundles a command and arguments to call later | ✔ | +| [Mediator](/behavioral/mediator/main.go) | Connects objects and acts as a proxy | ✔ | +| [Memento](/behavioral/memento/main.go) | Generate an opaque token that can be used to go back to a previous state | ✔ | | [Observer](/behavioral/observer.md) | Provide a callback for notification of events/changes to data | ✔ | | [Registry](/behavioral/registry.md) | Keep track of all subclasses of a given class | ✘ | -| [State](/behavioral/state.md) | Encapsulates varying behavior for the same object based on its internal state | ✘ | +| [State](/behavioral/state/main.go) | Encapsulates varying behavior for the same object based on its internal state | ✔ | | [Strategy](/behavioral/strategy.md) | Enables an algorithm's behavior to be selected at runtime | ✔ | -| [Template](/behavioral/template.md) | Defines a skeleton class which defers some methods to subclasses | ✘ | -| [Visitor](/behavioral/visitor.md) | Separates an algorithm from an object on which it operates | ✘ | +| [Template](/behavioral/template/main.go) | Defines a skeleton class which defers some methods to subclasses | ✔ | +| [Visitor](/behavioral/visitor/main.go) | Separates an algorithm from an object on which it operates | ✔ | ## Synchronization Patterns @@ -61,7 +61,7 @@ A curated collection of idiomatic design & application patterns for Go language. | Pattern | Description | Status | |:-------:|:----------- |:------:| -| [N-Barrier](/concurrency/barrier.md) | Prevents a process from proceeding until all N processes reach to the barrier | ✘ | +| [N-Barrier](/concurrency/n_barrier/main.go) | Prevents a process from proceeding until all N processes reach to the barrier | ✔ | | [Bounded Parallelism](/concurrency/bounded_parallelism.md) | Completes large number of independent tasks with resource limits | ✔ | | [Broadcast](/concurrency/broadcast.md) | Transfers a message to all recipients simultaneously | ✘ | | [Coroutines](/concurrency/coroutine.md) | Subroutines that allow suspending and resuming execution at certain locations | ✘ | diff --git a/behavioral/chain_of_responsibility/go_test.go b/behavioral/chain_of_responsibility/go_test.go new file mode 100644 index 0000000..536288c --- /dev/null +++ b/behavioral/chain_of_responsibility/go_test.go @@ -0,0 +1,42 @@ +package main + +import ( + "log" + "sync" + "testing" + "time" +) + +// 想弄清楚slice遍历时被修改到底会发生什么. +// 结果是安全失败. +// 并不意味着是线程安全的. +func TestGo(t *testing.T){ + s := make([]int ,0) + wg := &sync.WaitGroup{} + wg.Add(2) + for i:=0 ;i<10 ;i++{ + s = append(s, i) + } + + itv := time.Second + go func() { + defer wg.Done() + for k ,v := range s{ + println(k ,v) + time.Sleep(itv) + } + }() + + time.Sleep(itv) + + go func() { + defer wg.Done() + s = append(s ,810) + time.Sleep(itv) + s = append(s ,114) + log.Println("add") + }() + + wg.Wait() + log.Println(s) +} diff --git a/behavioral/chain_of_responsibility/main.go b/behavioral/chain_of_responsibility/main.go new file mode 100644 index 0000000..a8b0b7e --- /dev/null +++ b/behavioral/chain_of_responsibility/main.go @@ -0,0 +1,74 @@ +package main + +type GameType int + +const ( + TypeFPS GameType = 1 + TypeRPG = TypeFPS << 1 +) + +type Game interface { + Type() GameType + Start(player string) +} + +// chain of responsibility +type GameSelector struct { + GameList []Game +} + +func (g *GameSelector)AddGame(games ...Game){ + g.GameList = append(g.GameList ,games...) +} + +func (g GameSelector) Start(t GameType, player string) { + for _ ,v := range g.GameList{ + if v.Type() == t{ + v.Start(player) + return + } + } +} + +type FPSGame struct { + t GameType +} + +func (f FPSGame) Start(player string) { + println(player ,"join in fps game") +} + +func (f FPSGame)Type() GameType{ + return f.t +} + +type RPGGame struct { + t GameType +} + +func (RPGGame) Start(player string) { + println(player ,"join in rpg game") +} + +func (r RPGGame)Type() GameType{ + return r.t +} + +func main(){ + fps := FPSGame{TypeFPS} + rpg := RPGGame{TypeRPG} + + sl := GameSelector{} + sl.AddGame(fps ,rpg) + + player := "icg" + sl.Start(TypeRPG ,player) + println() + sl.Start(TypeFPS ,player) + // output: + /* + icg join in rpg game + + icg join in fps game + */ +} diff --git a/behavioral/command/main.go b/behavioral/command/main.go new file mode 100644 index 0000000..6747dd5 --- /dev/null +++ b/behavioral/command/main.go @@ -0,0 +1,82 @@ +// 命令模式(command pattern)主要解耦了行为和接收者,使之可以任意组合为一个完整的命令,并在合适的时候被调用 +// 主要分为四个部分,command ,ConcreteCommand ,receiver ,invoker. +// 分别对应以下GameCommand ,(CommandAttack|CommandEscape) ,GamePlayer ,Invoker. +package main + +// command +type GameCommand interface { + Execute() +} + +// ConcreteCommand +type CommandAttack struct {Player GamePlayer} + +func (c CommandAttack)Execute(){ + c.Player.Attack() +} + +// ConcreteCommand +type CommandEscape struct {Player GamePlayer} + +func (c CommandEscape)Execute(){ + c.Player.Escape() +} + +// receiver +type GamePlayer interface { + Attack() + Escape() +} + +type GunPlayer struct {Name string} + +func (g GunPlayer) Attack() { + println(g.Name ,"opened fire") +} + +func (g GunPlayer) Escape() { + println(g.Name ,"escape") +} + +// invoker +type CommandInvoker struct { + CommandList chan GameCommand +} + +func (in *CommandInvoker)CallCommands(){ + for{ + select { + case cmd := <- in.CommandList: + cmd.Execute() + default: + return + } + } +} + +func (in *CommandInvoker)PushCommands(c ...GameCommand){ + for _ ,v := range c{ + in.CommandList <- v + } +} + +func main(){ + invoker := &CommandInvoker{ + make(chan GameCommand ,10), + } + + playerA := GunPlayer{"icg"} + attk := CommandAttack{playerA} + escp := CommandEscape{playerA} + + invoker.PushCommands(attk ,escp ,escp ,attk ,escp) + invoker.CallCommands() + // output: + /* + icg opened fire + icg escape + icg escape + icg opened fire + icg escape + */ +} diff --git a/behavioral/mediator/main.go b/behavioral/mediator/main.go new file mode 100644 index 0000000..ac20b50 --- /dev/null +++ b/behavioral/mediator/main.go @@ -0,0 +1,76 @@ +// 中介者模式(mediator pattern) +// 当两个对象存在复杂的依赖关系时,考虑增加一个中介者使之解耦,使他们不需要进行显式的引用. +// 但问题在于中介本身会去实现复杂的逻辑,进而导致中介者变得复杂臃肿难以维护. +// +// 这里是竞价的例子. +// 参与竞价的玩家只需要向中介出价就可以知道是否能买到商品,由中介确定谁的出价高 +package main + +import ( + "log" + "math/rand" + "time" +) + +// buyer 之间通过中介进行引用 +type Buyer struct { + m *Mediator + Name string// unique +} + +func (b Buyer)Bid(price int){ + if p ,ok := b.m.isMaxPrice(b ,price);ok{ + log.Println(b.Name ,"are in lead temporarily with" ,p) + }else{ + log.Println(b.Name ,"lose with" ,p) + } +} + +type Mediator struct { + Buyers map[Buyer]int + MaxPrice int + MaxPriceBuyer Buyer +} + +func (m *Mediator)isMaxPrice(b Buyer ,price int)(int ,bool){ + if b.Name == m.MaxPriceBuyer.Name{ + return m.MaxPrice ,true + } + m.Buyers[b] = price + for k ,v := range m.Buyers{ + if v > m.MaxPrice{ + m.MaxPrice = v + m.MaxPriceBuyer = k + } + } + if b.Name == m.MaxPriceBuyer.Name{ + return price ,true + } + return price ,false +} + +func main(){ + m := &Mediator{ + make(map[Buyer]int), + 0, + Buyer{}, + } + + icg := Buyer{m ,"icg"} + nyn := Buyer{m ,"nyn"} + + rand.Seed(time.Now().UnixNano()) + for i:=0 ;i<3 ;i++{ + icg.Bid(rand.Intn(10000)) + nyn.Bid(rand.Intn(10000)) + } + // output: + /* + 2019/05/02 12:19:52 icg are in lead temporarily with 1024 + 2019/05/02 12:19:52 nyn are in lead temporarily with 4232 + 2019/05/02 12:19:52 icg are in lead temporarily with 6412 + 2019/05/02 12:19:52 nyn are in lead temporarily with 6747 + 2019/05/02 12:19:52 icg lose with 4951 + 2019/05/02 12:19:52 nyn are in lead temporarily with 6747 + */ +} diff --git a/behavioral/memento/main.go b/behavioral/memento/main.go new file mode 100644 index 0000000..047e635 --- /dev/null +++ b/behavioral/memento/main.go @@ -0,0 +1,92 @@ +// 备忘录模式 memento pattern +// 在不影响原结构封装的情况下,能够暂时保存一个结构的状态,并能够恢复 +// 这里是一个游戏存档的例子,尝试保存玩家当前位置,并在读档的时候恢复 +package main + +import ( + "container/list" + "log" +) + +// originator +type Player struct { + // 需要记录的数据可以考虑单独封装 + // type Pos struct{X,Y int} + X,Y int + + // other info + Name string +} + +func (p *Player)MoveTo(x,y int){ + p.X = x + p.Y = y +} + +func (p Player)Save()PlayerMemento{ + return PlayerMemento{ + X:p.X, + Y:p.Y, + } +} + +func (p *Player)Restore(m PlayerMemento){ + p.X = m.X + p.Y = m.Y +} + +// memento +type PlayerMemento struct { + X,Y int +} + +// caretaker +type PlayerCareTaker struct { + MementoList *list.List +} + +func (ct *PlayerCareTaker)AddMemento(memento PlayerMemento){ + ct.MementoList.PushFront(memento) +} + +func (ct *PlayerCareTaker)RemoveLast()PlayerMemento{ + ele := ct.MementoList.Front() + val := ct.MementoList.Remove(ele) + if memento ,ok := val.(PlayerMemento);ok{ + return memento + }else{ + return PlayerMemento{} + } +} + +func main(){ + ct := &PlayerCareTaker{list.New()} + icg := &Player{ + X:114, + Y:514, + Name:"icg", + } + ct.AddMemento(icg.Save()) + log.Println(icg.X ,icg.Y) + + icg.MoveTo(810 ,19) + log.Println(icg.X ,icg.Y) + ct.AddMemento(icg.Save()) + + icg.MoveTo(0 ,0) + log.Println(icg.X ,icg.Y) + + icg.Restore(ct.RemoveLast()) + log.Println(icg.X ,icg.Y) + + icg.Restore(ct.RemoveLast()) + log.Println(icg.X ,icg.Y) + /* + output: + 2019/05/02 18:18:03 114 514 + 2019/05/02 18:18:03 810 19 + 2019/05/02 18:18:03 0 0 + 2019/05/02 18:18:03 810 19 + 2019/05/02 18:18:03 114 514 + */ +} diff --git a/behavioral/state/main.go b/behavioral/state/main.go new file mode 100644 index 0000000..b586d18 --- /dev/null +++ b/behavioral/state/main.go @@ -0,0 +1,84 @@ +// 状态模式 state pattern. +// 与策略模式在一些场景可以通用,但策略模式倾向于调用者根据情况手动改变内部策略以切换算法, +// 状态模式倾向于由Context内部自行管理状态,只需要设定初始状态即可,不需要手动切换. +// +// 以下是以跷跷板seesaw为例,分为[左侧高LeftState|右侧高RightState] +package main + +import ( + "math/rand" + "time" +) + +// state +type SeesawState interface { + LiftLeftSide(S *Seesaw) + LiftRightSide(S *Seesaw) +} + +type LeftState struct {} + +func (LeftState) LiftLeftSide(S *Seesaw) { + println("↑LEFT > left side wads already lifted") +} + +func (l LeftState) LiftRightSide(S *Seesaw) { + println("RIGHT↑ > lift right side") + S.State = RightState{} +} + +type RightState struct {} + +func (r RightState) LiftLeftSide(S *Seesaw) { + println("↑LEFT > lift left side") + S.State = LeftState{} +} + +func (RightState) LiftRightSide(S *Seesaw) { + println("RIGHT↑ > right side wads already lifted") +} + +// context +type Seesaw struct { + State SeesawState +} + +func (s *Seesaw)MakeLeftUp(){ + s.State.LiftLeftSide(s) +} + +func (s *Seesaw)MakeRightUp(){ + s.State.LiftRightSide(s) +} + +func main(){ + // init left + seesaw := &Seesaw{ + State:LeftState{}, + } + + rand.Seed(time.Now().UnixNano()) + for i:=0 ;i<10 ;i++{ + if rand.Intn(2) == 1{ + // ▄▃▂ + seesaw.MakeLeftUp() + }else{ + // ▂▃▄ + seesaw.MakeRightUp() + } + } + /* + output: + RIGHT↑ > lift right side + RIGHT↑ > right side wads already lifted + RIGHT↑ > right side wads already lifted + ↑LEFT > lift left side + ↑LEFT > left side wads already lifted + ↑LEFT > left side wads already lifted + ↑LEFT > left side wads already lifted + RIGHT↑ > lift right side + RIGHT↑ > right side wads already lifted + ↑LEFT > lift left side + */ +} + diff --git a/behavioral/state/problem.go b/behavioral/state/problem.go new file mode 100644 index 0000000..159c2d6 --- /dev/null +++ b/behavioral/state/problem.go @@ -0,0 +1,118 @@ +// 存疑部分. +// +// 这里是以玩家的健康状态为基准,影响生命回复和受到伤害的例子,有三个状态[健康HealthyState|受伤WoundedState|死亡DeadState]. +// 和main.go中的跷跷板例子由行为驱动状态变化不同.以下例子的行为是受到数据影响的,结果就是达不到完全消除if-else的效果 +// +// 疑问: +// 1. 很多地方说状态模式可以消除if-else,事实上要做到自行的状态切换很多时候还是会用到if-else,这里的消除应当是指 +// 将巨大的条件语句拆分开(?) +// 2. 还是说这种由数据决定状态的事务逻辑下不适合使用状态模式 +package main + +import ( + "errors" + "fmt" +) + +// state +type PlayerState interface { + Heal(p *Player) error + Hurt(p *Player ,dmg int) +} + +type HealthyState struct {} + +func (h HealthyState)Heal(p *Player) error{ + return nil +} + +func (h HealthyState)Hurt(p *Player ,dmg int){ + if dmg > 0 && dmg < p.MaxHealth{ + p.Health = p.Health - dmg + p.State = WoundedState{} + }else if dmg > p.MaxHealth{ + p.Health = 0 + p.State = DeadState{} + } +} + +type WoundedState struct {} + +func (WoundedState)Heal(p *Player) error{ + if p.Health >= p.MaxHealth - 5{ + fmt.Printf("healing from %d to %d\n" ,p.Health ,p.MaxHealth) + p.State = HealthyState{} + p.Health = p.MaxHealth + }else{ + fmt.Printf("healing from %d to %d\n" ,p.Health ,p.Health+5) + p.Health = p.Health + 5 + } + return nil +} + +func (h WoundedState)Hurt(p *Player ,dmg int){ + if p.Health > dmg{ + p.Health = p.Health - dmg + }else { + p.State = DeadState{} + p.Health = 0 + } +} + +type DeadState struct {} + +func (DeadState)Heal(P *Player) error{ + return errors.New("you are dead") +} + +func (DeadState)Hurt(P *Player ,dmg int){} + +// context +type Player struct { + Health int + MaxHealth int + State PlayerState +} + +func (p *Player)HealPlayer()error{ + return p.State.Heal(p) +} + +func (p *Player)HurtPlayer(damage int){ + fmt.Printf("damage %d\n" ,damage) + p.State.Hurt(p ,damage) +} + +//func main(){ +// player := &Player{ +// Health:100, +// MaxHealth:100, +// State:HealthyState{}, +// } +// +// rand.Seed(time.Now().UnixNano()) +// for i:=0 ;i<10 ;i++{ +// if err := player.HealPlayer();err != nil{ +// fmt.Println(err) +// break +// } +// player.HurtPlayer(rand.Intn(30)) +// } +// /* +// output: +// damage 28 +// healing from 72 to 77 +// damage 29 +// healing from 48 to 53 +// damage 15 +// healing from 38 to 43 +// damage 1 +// healing from 42 to 47 +// damage 19 +// healing from 28 to 33 +// damage 27 +// healing from 6 to 11 +// damage 16 +// you are dead +// */ +//} diff --git a/behavioral/strategy/main.go b/behavioral/strategy/main.go new file mode 100644 index 0000000..84017ca --- /dev/null +++ b/behavioral/strategy/main.go @@ -0,0 +1,36 @@ +package main + +type Operator interface { + Apply(int, int) int +} + +// 通过定义内部的Operator实现不同策略的切换 +type Operation struct { + Operator Operator +} + +func (o *Operation) Operate(leftValue, rightValue int) int { + return o.Operator.Apply(leftValue, rightValue) +} + +type Addition struct{} + +func (Addition) Apply(lval, rval int) int { + return lval + rval +} + +type Multiplication struct{} + +func (Multiplication) Apply(lval, rval int) int { + return lval * rval +} + +func main(){ + a ,b := 2 ,4 + op := &Operation{ + Addition{}, + } + println(op.Operate(a ,b))// 6 + op.Operator = Multiplication{} + println(op.Operate(a ,b))// 8 +} diff --git a/behavioral/template/main.go b/behavioral/template/main.go new file mode 100644 index 0000000..d6aaa18 --- /dev/null +++ b/behavioral/template/main.go @@ -0,0 +1,101 @@ +// 存疑的部分. +// 模板模式和策略模式在很多时候给人感觉基本可以划等号,事实上两者侧重的方向不同. +// 策略模式倾向于通过改变结构的属性从而改变算法,模板模式则倾向于事先定义一个处理的流程,该流程是可以被整体替换的. +// 个人认为应该保证模板模式下的结构体应当独立出来,每个流程单独被调用. +// 感觉以下两种写法都有点别扭,待指正 +package main + +type Game interface { + OnStart() + OnEnd() +} + +type GameRunner struct {} + +func (GameRunner)Go(g Game){ + g.OnStart() + g.OnEnd() +} + +type FPSGame struct {} + +func (f FPSGame)OnStart(){ + println("fps game start") +} + +func (f FPSGame)OnEnd(){ + println("fps game end") +} + +type RPGGame struct {} + +func (RPGGame) OnStart() { + println("rpg game start") +} + +func (RPGGame) OnEnd() { + println("rpg game end") +} + +func main(){ + tpl := GameRunner{} + tpl.Go(FPSGame{}) + tpl.Go(RPGGame{}) + // output: + /* + fps game start + fps game end + rpg game start + rpg game end + */ +} + + +// ______________________________Another_______________ + +//type Game struct { +// OnStart func() +// OnEnd func() +//} +// +//func (g Game)Go(){ +// g.OnStart() +// g.OnEnd() +//} +// +//type FPSGame struct {Game} +// +//func NewFPSGame()FPSGame{ +// g := Game{ +// OnStart: func() { +// fmt.Println("start fps") +// time.Sleep(time.Second / 2) +// }, +// OnEnd: func() { +// fmt.Println("you are killed") +// }, +// } +// return FPSGame{g} +//} +// +//type RPGGame struct {Game} +// +//func NewRPGGame()RPGGame{ +// g := Game{ +// OnStart: func() { +// fmt.Println("start rpg") +// time.Sleep(time.Second / 2) +// }, +// OnEnd: func() { +// fmt.Println("you win") +// }, +// } +// return RPGGame{g} +//} +// +//func main(){ +// NewFPSGame().Go() +// println() +// NewRPGGame().Go() +//} + diff --git a/behavioral/visitor/main.go b/behavioral/visitor/main.go new file mode 100644 index 0000000..474c668 --- /dev/null +++ b/behavioral/visitor/main.go @@ -0,0 +1,110 @@ +// 访问者模式 visitor pattern. +// 该模式用于将数据结构和操作进行分离,同样用于分离操作的还有策略模式(strategy pattern),但两者存在侧重点的不同. +// 访问者模式侧重于扩展访问的方法类型,是为了对某一个类及其子类的访问方式进行扩展,允许增加更多不同的访问者,但不宜增加更多的Host. +// 以下以实体A(仅有玩家、NPC、物体三类)被访问(查看信息、发起挑战等,此处可扩展)的过程 +package main + +import "log" + +// 如果这里有更多的新类型Host需要扩展,则不宜使用访问者模式 +type Host interface { + Accept(Visitor) +} + +type PlayerHost struct { + Name string + Level int +} + +func (p PlayerHost)Accept(v Visitor){ + v.VisitPlayer(p) +} + +type NPCHost struct { + Name string + IsImmortal bool +} + +func (n NPCHost)Accept(v Visitor){ + v.VisitNPC(n) +} + +type ObjectHost struct { + Name string + Price int +} + +func (o ObjectHost)Accept(v Visitor){ + v.VisitObject(o) +} + +type Visitor interface { + VisitPlayer(PlayerHost) + VisitNPC(NPCHost) + VisitObject(ObjectHost) +} + +// 访问者允许有不同类型的访问者 +// 仅查看信息的访问者 +type InfoVisitor struct {} + +func (InfoVisitor) VisitPlayer(p PlayerHost) { + log.Printf("Player -> Name:%s ,Level:%d\n" ,p.Name ,p.Level) +} + +func (InfoVisitor) VisitNPC(n NPCHost) { + log.Printf("NPC -> Name:%s ,Immortal:%v\n" ,n.Name ,n.IsImmortal) +} + +func (InfoVisitor) VisitObject(o ObjectHost) { + log.Printf("Object -> Name:%s ,Price:%d\n" ,o.Name ,o.Price) +} + +// 发起攻击的访问者 +type AggressiveVisitor struct {} + +func (AggressiveVisitor) VisitPlayer(p PlayerHost) { + log.Printf("Attack %s\n" ,p.Name) +} + +func (AggressiveVisitor) VisitNPC(n NPCHost) { + log.Printf("Attack NPC %s\n" ,n.Name) +} + +func (AggressiveVisitor) VisitObject(o ObjectHost) { + log.Printf("Unsupported target %s\n" ,o.Name) +} + +func main(){ + infoVst := InfoVisitor{} + agrVst := AggressiveVisitor{} + + pA := PlayerHost{"icg" ,1} + pB := PlayerHost{"sz" ,2} + npc := NPCHost{"nyn" ,true} + obj := ObjectHost{"cake" ,19} + + hostList := []Host{pA ,npc ,obj ,pB} + + for _ ,v := range hostList{ + v.Accept(infoVst) + } + println() + for _ ,v := range hostList{ + v.Accept(agrVst) + } + /* + output: + 2019/05/04 10:00:49 Player -> Name:icg ,Level:1 + 2019/05/04 10:00:49 NPC -> Name:nyn ,Immortal:true + 2019/05/04 10:00:49 Object -> Name:cake ,Price:19 + 2019/05/04 10:00:49 Player -> Name:sz ,Level:2 + + 2019/05/04 10:00:49 Attack icg + 2019/05/04 10:00:49 Attack NPC nyn + 2019/05/04 10:00:49 Unsupported target cake + 2019/05/04 10:00:49 Attack sz + */ + +} + diff --git a/concurrency/n_barrier/main.go b/concurrency/n_barrier/main.go new file mode 100644 index 0000000..45276a2 --- /dev/null +++ b/concurrency/n_barrier/main.go @@ -0,0 +1,94 @@ +package main + +import ( + "log" + "sync" +) + +type IBarrier interface { + // error for timeout if need + Await() error +} + + +// once +type ChanBarrier struct { + waitCh chan struct{} + sign chan struct{} +} + +func NewChanBarrier(gos int)*ChanBarrier{ + b := &ChanBarrier{ + waitCh:make(chan struct{} ,gos-1), + sign:make(chan struct{}), + } + for i:=0;i speed" ,vi.MaxSpeed() >> 1 ,"km/h") +} + +// concrete implementor +type OldDriver struct {} + +func (OldDriver)Drive(vi VehicleAbstraction){ + println("old driver -> speed" ,vi.MaxSpeed() ,"km/h") +} + +func main(){ + old := OldDriver{} + rk := RookieDriver{} + v1 := Truck{old ,60} + v2 := Car{old ,130} + v3 := Truck{rk ,60} + v4 := Car{rk ,130} + list := []VehicleAbstraction{v1 ,v2 ,v3 ,v4} + for _ ,v := range list{ + v.Run() + } + /* + output: + truck begin to move... + old driver -> speed 60 km/h + car begin to move... + old driver -> speed 130 km/h + truck begin to move... + rookie driver -> speed 30 km/h + car begin to move... + rookie driver -> speed 65 km/h + */ + +} + + + + + + diff --git a/structural/composite/main.go b/structural/composite/main.go new file mode 100644 index 0000000..9f4e043 --- /dev/null +++ b/structural/composite/main.go @@ -0,0 +1,68 @@ +// 组合模式 composite pattern. +// 用于表示树形的结构,这里以一个web静态目录为例 +package main + +type File struct { + IsDir bool + Name string + ChildFile []*File +} + +func (f *File)AddChild(file ...*File){ + f.ChildFile = append(f.ChildFile ,file...) +} + +func checkFile(file *File) { + println("into dir ->" ,file.Name) + for _ ,v := range file.ChildFile{ + if v.IsDir{ + checkFile(v) + }else{ + println("dir ->" ,file.Name ,".fileName ->" ,v.Name) + } + } +} + +func main(){ + /* + static| + -js| + -jquery.js + -main| + -index.js + -login.js + -css| + -bootstrap.css + */ + static := &File{true ,"static" ,make([]*File ,0)} + js := &File{true ,"js" ,make([]*File ,0)} + css := &File{true ,"css" ,make([]*File ,0)} + static.AddChild(js ,css) + + jquery := &File{false ,"jquery.js" ,nil} + mjs := &File{true ,"main" ,make([]*File ,0)} + js.AddChild(jquery ,mjs) + + injs := &File{false ,"index.js" ,nil} + lojs := &File{false ,"login.js" ,nil} + mjs.AddChild(injs ,lojs) + + + btstrap := &File{false ,"bootstrap.css" ,nil} + css.AddChild(btstrap) + + checkFile(static) + /* + output: + into dir -> static + into dir -> js + dir -> js .fileName -> jquery.js + into dir -> main + dir -> main .fileName -> index.js + dir -> main .fileName -> login.js + into dir -> css + dir -> css .fileName -> bootstrap.css + */ +} + + diff --git a/structural/facade/main.go b/structural/facade/main.go new file mode 100644 index 0000000..04c1a5b --- /dev/null +++ b/structural/facade/main.go @@ -0,0 +1,76 @@ +// 外观模式 facade pattern. +// 为多个子模块提供一个统一的调用接口,子模块可以是同一接口的实现也可以不同. +// 实际上编写程序的时候很多地方不知不觉的会使用该模式. +// 这里以购买鸡蛋,牛奶,小麦粉为例,从代购处一次性购买三种食材而不需要分别访问三个商店, +// SellAll()方法还可以将三处接口包装为原子操作,在购买失败时进行回滚 +package main + +import ( + "errors" + "fmt" +) + +type Shop interface { + Sell() error +} + +type EggShop struct {} + +func (EggShop)Sell() error{ + return errors.New("no more eggs left") +} + +type MilkShop struct {} + +func (MilkShop)Sell()error{ + return errors.New("no more milk left") +} + +type WheatFlourShop struct {} + +func (WheatFlourShop)Sell()error{ + return errors.New("no more wheat flour left") +} + +type DealerFacade struct { + EgShop Shop + MkShop Shop + WfShop Shop +} + +func (d DealerFacade)BuyAll(){ + //if e := d.EgShop.Sell();e != nil{ + // log.Println(e) + // RollBack() + //} + //... + e1 := d.EgShop.Sell() + e2 := d.MkShop.Sell() + e3 := d.WfShop.Sell() + if e1 == nil && e2 == nil && e3 == nil{ + //success + }else{ + //fail and rollback + fmt.Printf("error:\n%v\n%v\n%v" ,e1 ,e2 ,e3) + } +} + +func main(){ + dealer := DealerFacade{ + EggShop{}, + MilkShop{}, + WheatFlourShop{}, + } + dealer.BuyAll() + /* + output: + error: + no more eggs left + no more milk left + no more wheat flour left + */ +} + + + + diff --git a/structural/flyweight/main.go b/structural/flyweight/main.go new file mode 100644 index 0000000..9316897 --- /dev/null +++ b/structural/flyweight/main.go @@ -0,0 +1,47 @@ +// 享元模式 flyweight pattern. +// 通过保存不同特征的实例以达到复用的效果,从而节省内存和优化性能. +// 与对象池模式的不同之处在于享元模式所保存的实例具有不同的特征,而对象池则全部是相同的实例. +// 这里以蛋糕为例,字段flavour为其味道,根据不同的味道作为不同的实例保存起来. +// 当实例的特征较少时,享元模式还可以和单例模式相结合. +package main + +type Cake struct { + Flavour string +} + +type CakeFactory struct { + Cakes map[string]Cake +} + +func (f CakeFactory)NewCake(flavour string)Cake{ + if c ,ok := f.Cakes[flavour];ok{ + println("get an existing" ,c.Flavour ,"cake from map") + }else{ + f.Cakes[flavour] = Cake{flavour} + println("put a new" ,flavour ,"cake into map") + } + return f.Cakes[flavour] +} + +func main(){ + factory := CakeFactory{make(map[string]Cake)} + + factory.NewCake("strawberry") + factory.NewCake("chocolates") + factory.NewCake("nynicg") + + factory.NewCake("strawberry") + factory.NewCake("nynicg") + factory.NewCake("chocolates") + /* + output: + put a new strawberry cake into map + put a new chocolates cake into map + put a new nynicg cake into map + get an existing strawberry cake from map + get an existing nynicg cake from map + get an existing chocolates cake from map + */ +} + +