Go 语言的设计模式之美。
Go 的设计模式:❌指在 Go 中不常用
创建型
单例模式:一个类只有一个实例
饿汉:系统初始化时创建并初始化单例对象
懒汉:调用实例时初始化单例对象
工厂模式:使用共同接口创建对象
建造者模式:与工厂不同在于,创建参数复杂的对象
原型模式:利用已有对象复制的方式来创建新的对象 ❌
结构型
代理模式:为其他对象提供⼀种代理以控制这个对象的访问 代理访问
桥接模式:将类的抽象部分和它的实现部分分离开来,使它们可以独立地变化 抽象实现拆分独立
装饰模式:动态地给⼀个对象添加⼀些额外的职责 附加职责
适配器模式:将⼀个类的接口转换成用户希望得到的另⼀个接口 转换接口
门面模式:定义⼀个高层接口,为子系统中的⼀组接口提供一个一致的外观 对外统一接口 ❌
组合模式:将对象组合成树型结构以表示”整体 - 部分”的层次结构 树型目录结构 ❌
享元模式:提供支持大量细粒度对象共享的有效方法 ❌
行为型
观察者模式:定义对象间的⼀种⼀对多的依赖关系,当⼀个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新
模板模式:定义⼀个操作中的算法骨架,而将⼀些步骤延迟到子类中,使得子类可以不改变⼀个算法的结构即可重新定义算法的某些特定步骤
策略模式:定义⼀系列算法,把它们⼀个个封装起来,并且使它们之间可互相替换,从而让算法可以独立于使用它的用户而变化
职责链模式:通过给多个对象处理请求的机会,减少请求的发送者与接收者之间的耦合。将接收对象链接起来,在链中传递请求,直到有⼀个对象处理这个请求 传递职责
状态模式:允许⼀个对象在其内部状态改变时改变它的行为
迭代器模式:提供⼀种方法来顺序访问⼀个聚合对象中的各个元素而不需要暴露该对象的内部表示
访问者模式:表示⼀个作用于某对象结构中的各元素的操作,使得在不改变各元素的类的前提下定义作用于这些元素的新操作 ❌
备忘录模式:在不破坏封装性的前提下,捕获⼀个对象的内部状态,并在该对象之外保存这个状态,从而可用在以后将该对象恢复到原先保存的状态 ❌
命令模式:将⼀个请求封装为⼀个对象,从而可用不同的请求对客户进行参数化,将请求排队或记录请求⽇志,支持可撤销的操作 ❌
解释器模式:给定⼀种语言,定义它的文法表示,并定义⼀个解释器,该解释器用来根据文法表示来解释语言中的句子 ❌
中介模式:用⼀个中介对象来封装⼀系列的对象交互。它使各对象不需要显式地相互调用,从而达到低耦合,还可以独立地改变对象间的交互 ❌
创建型
单例模式 Singleton 一个类只允许创建一个对象(实例) ,这个类就是单例类,这种设计模式就叫单例模式用处: 业务上,有些数据在系统中只应该保存一份
饿汉式
在系统初始化的时候我们已经把对象创建好了,需要用的时候直接拿过来用就好了
类一旦加载,就把单例初始化完成,保证 getInstance
的时候,单例是已经存在的了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package singletontype Singleton struct {}var singleton *Singletonfunc init () { singleton = &Singleton{} } func GetInstance () *Singleton { return singleton }
懒汉式(双重检测)
创建对象时比较懒,先不急着创建对象,在需要加载配置文件的时候再去创建
只有当调用 getInstance
的时候,才会去初始化这个单例。
为了实现懒汉式不可避免的需要加锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package singletonimport "sync" var ( lazySingleton *Singleton once = &sync.Once{} ) func GetLazyInstance () *Singleton { if lazySingleton == nil { once.Do(func () { lazySingleton = &Singleton{} }) } return lazySingleton }
工厂模式 Factory 在工厂模式中,在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象 。
简单工厂 Go 本身是没有构造函数的,一般而言采用 NewName
的方式创建对象/接口,当它返回的是接口的时候,其实就是简单工厂模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package factorytype IRuleConfigParser interface { Parse(data []byte ) } type jsonRuleConfigParser struct {} func (J jsonRuleConfigParser) Parse(data []byte ) { panic ("implement me" ) } type yamlRuleConfigParser struct {} func (Y yamlRuleConfigParser) Parse(data []byte ) { panic ("implement me" ) } func NewIRuleConfigParser (t string ) IRuleConfigParser { switch t { case "json" : return jsonRuleConfigParser{} case "yaml" : return yamlRuleConfigParser{} } return nil }
工厂方法 当对象的创建逻辑比较复杂,不只是简单的 new 一下就可以,而是要组合其他类对象。
做各种初始化操作的时候,推荐使用工厂方法模式,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂。
组合复用原则:优先使用组合 contains a(聚合 has a),而不是继承 is a 来达到目的
迪米特法则:⼀个对象应当对其他对象有尽可能少的了解,即不和陌生人说话
优点:降低类之间的耦合
缺点:产生大量中介类,设计变复杂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 type IRuleConfigParserFactory interface { CreateParser() IRuleConfigParser } type yamlRuleConfigParserFactory struct {} func (y yamlRuleConfigParserFactory) CreateParser() IRuleConfigParser { return yamlRuleConfigParser{} } type jsonRuleConfigParserFactory struct {} func (j jsonRuleConfigParserFactory) CreateParser() IRuleConfigParser { return jsonRuleConfigParser{} } func NewIRuleConfigParserFactory (t string ) IRuleConfigParserFactory { switch t { case "json" : return jsonRuleConfigParserFactory{} case "yaml" : return yamlRuleConfigParserFactory{} } return nil }
建造者模式 Builder 在 Golang 中对于创建类参数比较多的对象的时候,常见的做法是必填参数直接传递,可选参数通过传递可变的方法进行创建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package builderimport "fmt" type ResourcePoolConfigOption struct { maxTotal int maxIdle int minIdle int } type ResourcePoolConfigOptFunc func (option *ResourcePoolConfigOption) func NewResourcePoolConfig (name string , opts ...ResourcePoolConfigOptFunc) (*ResourcePoolConfig, error ) { if name == "" { return nil , fmt.Errorf("name can not be empty" ) } option := &ResourcePoolConfigOption{ maxTotal: 10 , maxIdle: 9 , minIdle: 1 , } for _, opt := range opts { opt(option) } if option.maxTotal < 0 || option.maxIdle < 0 || option.minIdle < 0 { return nil , fmt.Errorf("args err, option: %v" , option) } if option.maxTotal < option.maxIdle || option.minIdle > option.maxIdle { return nil , fmt.Errorf("args err, option: %v" , option) } return &ResourcePoolConfig{ name: name, maxTotal: option.maxTotal, maxIdle: option.maxIdle, minIdle: option.minIdle, }, nil }
原型模式 Prototype 利用已有对象(原型)进行复制(拷贝)的方式来创建新的对象,达到节省创建时间的目的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package prototypeimport ( "encoding/json" "time" ) type Keyword struct { word string visit int UpdatedAt *time.Time } func (k *Keyword) Clone() *Keyword { var newKeyword Keyword b, _ := json.Marshal(k) json.Unmarshal(b, &newKeyword) return &newKeyword } type Keywords map [string ]*Keywordfunc (words Keywords) Clone(updatedWords []*Keyword) Keywords { newKeywords := Keywords{} for k, v := range words { newKeywords[k] = v } for _, word := range updatedWords { newKeywords[word.word] = word.Clone() } return newKeywords }
结构型 代理模式 Proxy 为其他对象提供⼀种代理以控制这个对象的访问。
静态代理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package proxyimport ( "log" "time" ) type IUser interface { Login(username, password string ) error } type User struct {} func (u *User) Login(username, password string ) error { return nil } type UserProxy struct { user *User } func NewUserProxy (user *User) *UserProxy { return &UserProxy{ user: user, } } func (p *UserProxy) Login(username, password string ) error { start := time.Now() if err := p.user.Login(username, password); err != nil { return err } log.Printf("user login cost time: %s" , time.Now().Sub(start)) return nil }
桥接模式 Bridge 将类的抽象部分和它的实现部分分离开来,使它们可以独立地变化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package bridgetype IMsgSender interface { Send(msg string ) error } type EmailMsgSender struct { emails []string } func NewEmailMsgSender (emails []string ) *EmailMsgSender { return &EmailMsgSender{emails: emails} } func (s *EmailMsgSender) Send(msg string ) error { return nil } type INotification interface { Notify(msg string ) error } type ErrorNotification struct { sender IMsgSender } func NewErrorNotification (sender IMsgSender) *ErrorNotification { return &ErrorNotification{sender: sender} } func (n *ErrorNotification) Notify(msg string ) error { return n.sender.Send(msg) }
装饰模式 Decorator 动态地给⼀个对象添加⼀些额外的职责。它提供了用子类扩展功能的⼀个灵活的替代,比派生一个子类更加灵活。
附加职责
下面是一个简单的画画的例子,默认的 Square
只有基础的画画功能, ColorSquare
为他加上了颜色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package decoratortype IDraw interface { Draw() string } type Square struct {}func (s Square) Draw() string { return "this is a square" } type ColorSquare struct { square IDraw color string } func NewColorSquare (square IDraw, color string ) ColorSquare { return ColorSquare{color: color, square: square} } func (c ColorSquare) Draw() string { return c.square.Draw() + ", color is " + c.color }
适配器模式 Adapter 将⼀个类的接口转换成用户希望得到的另⼀个接口,它使原本不相容的接口得以协同⼯作。
假设现在有一个运维系统,需要分别调用阿里云和 AWS 的 SDK 创建主机,两个 SDK 提供的创建主机的接口不一致,此时就可以通过适配器模式,将两个接口统一。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package adapterimport "fmt" type ICreateServer interface { CreateServer(cpu, mem float64 ) error } type AWSClient struct {}func (c *AWSClient) RunInstance(cpu, mem float64 ) error { fmt.Printf("aws client run success, cpu: %f, mem: %f" , cpu, mem) return nil } type AwsClientAdapter struct { Client AWSClient } func (a *AwsClientAdapter) CreateServer(cpu, mem float64 ) error { a.Client.RunInstance(cpu, mem) return nil } type AliyunClient struct {}func (c *AliyunClient) CreateServer(cpu, mem int ) error { fmt.Printf("aws client run success, cpu: %d, mem: %d" , cpu, mem) return nil } type AliyunClientAdapter struct { Client AliyunClient } func (a *AliyunClientAdapter) CreateServer(cpu, mem float64 ) error { a.Client.CreateServer(int (cpu), int (mem)) return nil }
门面模式 Facade 定义⼀个高层接口,为子系统中的⼀组接口提供一个一致的外观,从而简化了该子系统的使用。
对外统一接口
假设现在我有一个网站,以前有登录和注册的流程,登录的时候调用用户的查询接口,注册时调用用户的创建接口。为了简化用户的使用流程,我们现在提供直接验证码登录/注册的功能,如果该手机号已注册那么我们就走登录流程,如果该手机号未注册,那么我们就创建一个新的用户。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package facadetype IUser interface { Login(phone int , code int ) (*User, error ) Register(phone int , code int ) (*User, error ) } type IUserFacade interface { LoginOrRegister(phone int , code int ) error } type User struct { Name string } type UserService struct {}func (u UserService) Login(phone int , code int ) (*User, error ) { return &User{Name: "test login" }, nil } func (u UserService) Register(phone int , code int ) (*User, error ) { return &User{Name: "test register" }, nil } func (u UserService) LoginOrRegister(phone int , code int ) (*User, error ) { user, err := u.Login(phone, code) if err != nil { return nil , err } if user != nil { return user, nil } return u.Register(phone, code) }
组合模式 Composite 将对象组合成树型结构以表示“整体 - 部分”的层次结构,使得用户对单个对象和组合对象的使用具有⼀致性。
树形目录结构
公司的人员组织就是一个典型的树状的结构,现在假设我们现在有部分,和员工,两种角色,一个部门下面可以存在子部门和员工,员工下面不能再包含其他节点。
我们现在要实现一个统计一个部门下员工数量的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package compositetype IOrganization interface { Count() int } type Employee struct { Name string } func (Employee) Count() int { return 1 } type Department struct { Name string SubOrganizations []IOrganization } func (d Department) Count() int { c := 0 for _, org := range d.SubOrganizations { c += org.Count() } return c } func (d *Department) AddSub(org IOrganization) { d.SubOrganizations = append (d.SubOrganizations, org) } func NewOrganization () IOrganization { root := &Department{Name: "root" } for i := 0 ; i < 10 ; i++ { root.AddSub(&Employee{}) root.AddSub(&Department{Name: "sub" , SubOrganizations: []IOrganization{&Employee{}}}) } return root }
享元模式 Flyweight 提供支持大量细粒度对象共享的有效方法。
象棋,无论是什么对局,棋子的基本属性其实是固定的,并不会因为随着下棋的过程变化。那我们就可以把棋子变为享元,让所有的对局都共享这些对象,以此达到节省内存的目的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 package flyweightvar units = map [int ]*ChessPieceUnit{ 1 : { ID: 1 , Name: "車" , Color: "red" , }, 2 : { ID: 2 , Name: "炮" , Color: "red" , }, } type ChessPieceUnit struct { ID uint Name string Color string } func NewChessPieceUnit (id int ) *ChessPieceUnit { return units[id] } type ChessPiece struct { Unit *ChessPieceUnit X int Y int } type ChessBoard struct { chessPieces map [int ]*ChessPiece } func NewChessBoard () *ChessBoard { board := &ChessBoard{chessPieces: map [int ]*ChessPiece{}} for id := range units { board.chessPieces[id] = &ChessPiece{ Unit: NewChessPieceUnit(id), X: 0 , Y: 0 , } } return board } func (c *ChessBoard) Move(id, x, y int ) { c.chessPieces[id].X = x c.chessPieces[id].Y = y }
行为型 观察者模式 Observer 定义对象间的⼀种⼀对多的依赖关系,当⼀个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package observerimport "fmt" type ISubject interface { Register(observer IObserver) Remove(observer IObserver) Notify(msg string ) } type IObserver interface { Update(msg string ) } type Subject struct { observers []IObserver } func (sub *Subject) Register(observer IObserver) { sub.observers = append (sub.observers, observer) } func (sub *Subject) Remove(observer IObserver) { for i, ob := range sub.observers { if ob == observer { sub.observers = append (sub.observers[:i], sub.observers[i+1 :]...) } } } func (sub *Subject) Notify(msg string ) { for _, o := range sub.observers { o.Update(msg) } } type Observer1 struct {}func (Observer1) Update(msg string ) { fmt.Printf("Observer1: %s" , msg) } type Observer2 struct {}func (Observer2) Update(msg string ) { fmt.Printf("Observer2: %s" , msg) }
模板模式 Template 定义⼀个操作中的算法骨架,而将⼀些步骤延迟到子类中,使得子类可以不改变⼀个算法的结构即可重新定义算法的某些特定步骤。
要做一个短信推送的系统,那么需要
检查短信字数是否超过限制
检查手机号是否正确
发送短信
返回状态
我们可以发现,在发送短信的时候由于不同的供应商调用的接口不同,所以会有一些实现上的差异,但是他的算法(业务逻辑)是固定的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package templateimport "fmt" type ISMS interface { send(content string , phone int ) error } type sms struct { ISMS } func (s *sms) Valid(content string ) error { if len (content) > 63 { return fmt.Errorf("content is too long" ) } return nil } func (s *sms) Send(content string , phone int ) error { if err := s.Valid(content); err != nil { return err } return s.send(content, phone) } type TelecomSms struct { *sms } func NewTelecomSms () *TelecomSms { tel := &TelecomSms{} tel.sms = &sms{ISMS: tel} return tel } func (tel *TelecomSms) send(content string , phone int ) error { fmt.Println("send by telecom success" ) return nil }
策略模式 Strategy 定义⼀系列算法,把它们⼀个个封装起来,并且使它们之间可互相替换,从而让算法可以独立于使用它的用户而变化。
在保存文件的时候,由于政策或者其他的原因可能需要选择不同的存储方式,敏感数据我们需要加密存储,不敏感的数据我们可以直接明文保存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package strategyimport ( "fmt" "io/ioutil" "os" ) type StorageStrategy interface { Save(name string , data []byte ) error } var strategys = map [string ]StorageStrategy{ "file" : &fileStorage{}, "encrypt_file" : &encryptFileStorage{}, } func NewStorageStrategy (t string ) (StorageStrategy, error ) { s, ok := strategys[t] if !ok { return nil , fmt.Errorf("not found StorageStrategy: %s" , t) } return s, nil } type fileStorage struct {}func (s *fileStorage) Save(name string , data []byte ) error { return ioutil.WriteFile(name, data, os.ModeAppend) } type encryptFileStorage struct {}func (s *encryptFileStorage) Save(name string , data []byte ) error { data, err := encrypt(data) if err != nil { return err } return ioutil.WriteFile(name, data, os.ModeAppend) } func encrypt (data []byte ) ([]byte , error ) { return data, nil }
职责链模式 Chain of Responsibility 通过给多个对象处理请求的机会,减少请求的发送者与接收者之间的耦合。将接收对象链接起来,在链中传递请 求,直到有⼀个对象处理这个请求。传递职责
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package chaintype SensitiveWordFilter interface { Filter(content string ) bool } type SensitiveWordFilterChain struct { filters []SensitiveWordFilter } func (c *SensitiveWordFilterChain) AddFilter(filter SensitiveWordFilter) { c.filters = append (c.filters, filter) } func (c *SensitiveWordFilterChain) Filter(content string ) bool { for _, filter := range c.filters { if filter.Filter(content) { return true } } return false } type AdSensitiveWordFilter struct {}func (f *AdSensitiveWordFilter) Filter(content string ) bool { return false } type PoliticalWordFilter struct {}func (f *PoliticalWordFilter) Filter(content string ) bool { return true }
状态模式 State 允许⼀个对象在其内部状态改变时改变它的行为。
状态变成类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 package stateimport "fmt" type Machine struct { state IState } func (m *Machine) SetState(state IState) { m.state = state } func (m *Machine) GetStateName() string { return m.state.GetName() } func (m *Machine) Approval() { m.state.Approval(m) } func (m *Machine) Reject() { m.state.Reject(m) } type IState interface { Approval(m *Machine) Reject(m *Machine) GetName() string } type leaderApproveState struct {}func (leaderApproveState) Approval(m *Machine) { fmt.Println("leader 审批成功" ) m.SetState(GetFinanceApproveState()) } func (leaderApproveState) GetName() string { return "LeaderApproveState" } func (leaderApproveState) Reject(m *Machine) {}func GetLeaderApproveState () IState { return &leaderApproveState{} } type financeApproveState struct {}func (f financeApproveState) Approval(m *Machine) { fmt.Println("财务审批成功" ) fmt.Println("出发打款操作" ) } func (f financeApproveState) Reject(m *Machine) { m.SetState(GetLeaderApproveState()) } func (f financeApproveState) GetName() string { return "FinanceApproveState" } func GetFinanceApproveState () IState { return &financeApproveState{} }
迭代器模式 Iterator 提供⼀种⽅法来顺序访问⼀个聚合对象中的各个元素而不需要暴露该对象的内部表示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package iteratortype Iterator interface { HasNext() bool Next() CurrentItem() interface {} } type ArrayInt []int func (a ArrayInt) Iterator() Iterator { return &ArrayIntIterator{ arrayInt: a, index: 0 , } } type ArrayIntIterator struct { arrayInt ArrayInt index int } func (iter *ArrayIntIterator) HasNext() bool { return iter.index < len (iter.arrayInt)-1 } func (iter *ArrayIntIterator) Next() { iter.index++ } func (iter *ArrayIntIterator) CurrentItem() interface {} { return iter.arrayInt[iter.index] }
访问者模式 Visitor 表示⼀个作用于某对象结构中的各元素的操作,使得在不改变各元素的类的前提下定义作用于这些元素的新操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 package visitorimport ( "fmt" "path" ) type Visitor interface { Visit(IResourceFile) error } type IResourceFile interface { Accept(Visitor) error } func NewResourceFile (filepath string ) (IResourceFile, error ) { switch path.Ext(filepath) { case ".ppt" : return &PPTFile{path: filepath}, nil case ".pdf" : return &PdfFile{path: filepath}, nil default : return nil , fmt.Errorf("not found file type: %s" , filepath) } } type PdfFile struct { path string } func (f *PdfFile) Accept(visitor Visitor) error { return visitor.Visit(f) } type PPTFile struct { path string } func (f *PPTFile) Accept(visitor Visitor) error { return visitor.Visit(f) } type Compressor struct {}func (c *Compressor) Visit(r IResourceFile) error { switch f := r.(type ) { case *PPTFile: return c.VisitPPTFile(f) case *PdfFile: return c.VisitPDFFile(f) default : return fmt.Errorf("not found resource typr: %##v" , r) } } func (c *Compressor) VisitPPTFile(f *PPTFile) error { fmt.Println("this is ppt file" ) return nil } func (c *Compressor) VisitPDFFile(f *PdfFile) error { fmt.Println("this is pdf file" ) return nil }
备忘录模式 Memento 在不破坏封装性的前提下,捕获⼀个对象的内部状态,并在该对象之外保存这个状态,从而可用在以后将该对象恢复到原先保存的状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package mementotype InputText struct { content string } func (in *InputText) Append(content string ) { in.content += content } func (in *InputText) GetText() string { return in.content } func (in *InputText) Snapshot() *Snapshot { return &Snapshot{content: in.content} } func (in *InputText) Restore(s *Snapshot) { in.content = s.GetText() } type Snapshot struct { content string } func (s *Snapshot) GetText() string { return s.content }
命令模式 Command 将⼀个请求封装为⼀个对象,从而可用不同的请求对客户进行参数化,将请求排队或记录请求⽇志,支持可撤销的操作。
日志记录,可撤销
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package commandimport "fmt" type ICommand interface { Execute() error } type StartCommand struct {}func NewStartCommand ( /*正常情况下这里会有一些参数*/ ) *StartCommand { return &StartCommand{} } func (c *StartCommand) Execute() error { fmt.Println("game start" ) return nil } type ArchiveCommand struct {}func NewArchiveCommand ( /*正常情况下这里会有一些参数*/ ) *ArchiveCommand { return &ArchiveCommand{} } func (c *ArchiveCommand) Execute() error { fmt.Println("game archive" ) return nil }
解释器模式 Interpreter 给定⼀种语言,定义它的文法表示,并定义⼀个解释器,该解释器用来根据文法表示来解释语言中的句子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 package interpreterimport ( "fmt" "regexp" "strconv" "strings" ) type AlertRule struct { expression IExpression } func NewAlertRule (rule string ) (*AlertRule, error ) { exp, err := NewAndExpression(rule) return &AlertRule{expression: exp}, err } func (r AlertRule) Interpret(stats map [string ]float64 ) bool { return r.expression.Interpret(stats) } type IExpression interface { Interpret(stats map [string ]float64 ) bool } type GreaterExpression struct { key string value float64 } func (g GreaterExpression) Interpret(stats map [string ]float64 ) bool { v, ok := stats[g.key] if !ok { return false } return v > g.value } func NewGreaterExpression (exp string ) (*GreaterExpression, error ) { data := regexp.MustCompile(`\s+` ).Split(strings.TrimSpace(exp), -1 ) if len (data) != 3 || data[1 ] != ">" { return nil , fmt.Errorf("exp is invalid: %s" , exp) } val, err := strconv.ParseFloat(data[2 ], 10 ) if err != nil { return nil , fmt.Errorf("exp is invalid: %s" , exp) } return &GreaterExpression{ key: data[0 ], value: val, }, nil } type LessExpression struct { key string value float64 } func (g LessExpression) Interpret(stats map [string ]float64 ) bool { v, ok := stats[g.key] if !ok { return false } return v < g.value } func NewLessExpression (exp string ) (*LessExpression, error ) { data := regexp.MustCompile(`\s+` ).Split(strings.TrimSpace(exp), -1 ) if len (data) != 3 || data[1 ] != "<" { return nil , fmt.Errorf("exp is invalid: %s" , exp) } val, err := strconv.ParseFloat(data[2 ], 10 ) if err != nil { return nil , fmt.Errorf("exp is invalid: %s" , exp) } return &LessExpression{ key: data[0 ], value: val, }, nil } type AndExpression struct { expressions []IExpression } func (e AndExpression) Interpret(stats map [string ]float64 ) bool { for _, expression := range e.expressions { if !expression.Interpret(stats) { return false } } return true } func NewAndExpression (exp string ) (*AndExpression, error ) { exps := strings.Split(exp, "&&" ) expressions := make ([]IExpression, len (exps)) for i, e := range exps { var expression IExpression var err error switch { case strings.Contains(e, ">" ): expression, err = NewGreaterExpression(e) case strings.Contains(e, "<" ): expression, err = NewLessExpression(e) default : err = fmt.Errorf("exp is invalid: %s" , exp) } if err != nil { return nil , err } expressions[i] = expression } return &AndExpression{expressions: expressions}, nil }
用⼀个中介对象来封装⼀系列的对象交互。它使各对象不需要显式地相互调用,从而达到低耦合,还可以独立地改变对象间的交互。
不直接引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package mediatorimport ( "fmt" "reflect" ) type Input string func (i Input) String() string { return string (i) } type Selection string func (s Selection) Selected() string { return string (s) } type Button struct { onClick func () } func (b *Button) SetOnClick(f func () ) { b.onClick = f } type IMediator interface { HandleEvent(component interface {}) } type Dialog struct { LoginButton *Button RegButton *Button Selection *Selection UsernameInput *Input PasswordInput *Input RepeatPasswordInput *Input } func (d *Dialog) HandleEvent(component interface {}) { switch { case reflect.DeepEqual(component, d.Selection): if d.Selection.Selected() == "登录" { fmt.Println("select login" ) fmt.Printf("show: %s\n" , d.UsernameInput) fmt.Printf("show: %s\n" , d.PasswordInput) } else if d.Selection.Selected() == "注册" { fmt.Println("select register" ) fmt.Printf("show: %s\n" , d.UsernameInput) fmt.Printf("show: %s\n" , d.PasswordInput) fmt.Printf("show: %s\n" , d.RepeatPasswordInput) } } }