[TOC] ## 设计模式 ### 简单工厂类 <details> <summary>main.go</summary> ``` package main type Product interface { SetName(name string) GetName() string } type Product1 struct { name string } func (p *Product1) SetName(name string) { p.name=name } func (p Product1) GetName() string { return "Product1's name"+p.name } type Product2 struct { name string } func (p *Product2) SetName(name string) { p.name=name } func (p Product2) GetName() string { return "Product2's name"+p.name } type productType int const ( p1 productType=iota p2 ) type productFactory struct { } func (pf productFactory) Create(productType productType) Product{ switch productType { case p1: return &Product1{} case p2: return &Product2{} default: return nil } } ``` </details> <br /> 优点:实现了解耦 缺点:违背 "开闭原则" 适合:创建的对象比较少 ### 工厂方法模式 格式 <details> <summary>main.go</summary> ``` package main import "fmt" type Product interface { SetName(name string) GetName() string } type Product1 struct { name string } func (p *Product1) SetName(name string) { p.name=name } func (p Product1) GetName() string { return "Product1's name "+p.name } type Product2 struct { name string } func (p *Product2) SetName(name string) { p.name=name } func (p Product2) GetName() string { return "Product2's name "+p.name } //实现一个抽象工厂 type ProductFactory interface { Create() Product } //实现一个具体的工:产品1的工厂 type ProductFactory1 struct { } func (p *ProductFactory1) Create() Product { return &Product1{} } //实现一个具体的工:产品2的工厂 type ProductFactory2 struct { } func (p *ProductFactory2) Create() Product { return &Product2{} } func main() { productFactory:=&ProductFactory1{} //productFactory:=&ProductFactory2{} product := productFactory.Create() product.SetName("p1") fmt.Printf("%+v\n", product.GetName()) } ``` </details> <br /> 缓存示例 <details> <summary>main.go</summary> ``` package main import ( "fmt" ) type Cache interface { Set(key,value string) Get(key string) string } type RedisCache struct { data map[string]string } func (r *RedisCache) Set(key, value string) { r.data[key]=value } func (r RedisCache) Get(key string) string { return r.data[key] } func NewRedisCache() *RedisCache{ return &RedisCache{ data: make(map[string]string), } } type Memcached struct { data map[string]string } func (r *Memcached) Set(key, value string) { r.data[key]=value } func (r Memcached) Get(key string) string { return r.data[key] } func NewMemcached() *Memcached{ return &Memcached{ data: make(map[string]string), } } type CacheFactory interface { Create() Cache } //具体redis 工厂 type RedisCacheFactory struct { } func (r RedisCacheFactory) Create() Cache { return NewRedisCache() } //具体memcached 工厂 type MemCachedFactory struct { } func (r MemCachedFactory) Create() Cache { return NewMemcached() } func main() { CacheFactory :=&RedisCacheFactory{} //CacheFactory:=&MemCachedFactory{} cache := CacheFactory.Create() cache.Set("a","1") #### fmt.Printf("%+v\n", cache.Get("a")) } ``` </details> <br /> 优点: 保持了简单工厂模式的优点,而且克服了它的缺点 缺点: 在添加新产品时,在一定程度上增加了系统的复杂度 适合: 客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可 ### 值选项模式 > [参考](https://segmentfault.com/a/1190000013653494) <details> <summary>main.go</summary> ``` import ( "log" "time" ) type who struct { name string age int timeout time.Duration key int value string } var defaultMyFuncOptions = who{ name: "cpj", age: 0, timeout: time.Second, key: 1, value:"idcpj", } type whoOption func(options *who) func WithOptionAge(age int) whoOption { return func(options *who) { options.age=age } } func WithOptionKeyAndValue(key int, value string) whoOption { return func(options *who) { options.key = key options.value = value } } func NewWho(name string, opts ...whoOption) who { options := defaultMyFuncOptions options.name= name for _, o := range opts { o(&options) } return options } func (w *who) print(){ log.Println(w) } func main() { w := NewWho("idcpj", WithOptionAge(10), WithOptionKeyAndValue(12, "12"), ) w.print()//&{idcpj 10 1000000000 12 12} } ``` </details> <br /> ### 装饰模式(添加钩子) 格式: <details> <summary>main.go</summary> ``` package main import "fmt" //定义抽象的组件 type Component interface { Operate() } type Component1 struct { } func (c Component1) Operate() { fmt.Printf("%+v\n", "c1 operate") } // 定义一个抽象的装饰者 type Decorator interface { Component Do() //装饰行为,可以有多个 } //定义一个具体的装饰器 type Decorator1 struct { c Component } func (d Decorator1) Do() { fmt.Printf("%+v\n", "c1 发生了装饰行为") } // 重新实现component 的方法 func (d Decorator1) Operate() { d.Do() d.c.Operate() d.Do() } func main() { //无装饰模式 c1:=&Component1{} c1.Operate() //装饰模式 c2 :=&Decorator1{ c: c1, } c2.Operate() } ``` </details> <br /> ### 组合模式 一个具有层级关系的对象由一系列拥有父子关系的对象通过树形结构组成(类似树形式的组件) > [参考](http://tigerb.cn/2020/04/06/go-patterns-component/?utm_source=tuicool&utm_medium=referral) > ![UTOOLS1589241072846.png](http://yanxuan.nosdn.127.net/5a119611178cd768ade28de3c7ad4bc9.png) ``` 成员属性 ChildComponents: 子组件列表 -> 稳定不变的 成员方法 Mount: 添加一个子组件 -> 稳定不变的 Remove: 移除一个子组件 -> 稳定不变的 Do: 执行组件&子组件 -> 变化的 ``` <details> <summary>main.go</summary> ``` package main import ( "context" "fmt" "reflect" "runtime" ) // Context 上下文 //type Context struct{} // IComponent 组件接口 type IComponent interface { // 添加一个子组件 Mount(c IComponent, components ...IComponent) error // 移除一个子组件 Remove(c IComponent) error // 执行组件&子组件 Do(ctx context.Context) error } // BaseComponent 基础组件 // 实现Add:添加一个子组件 // 实现Remove:移除一个子组件 type BaseComponent struct { // 子组件列表 ChildComponents []IComponent } // Mount 挂载一个子组件 func (bc *BaseComponent) Mount(c IComponent, components ...IComponent) (err error) { bc.ChildComponents = append(bc.ChildComponents, c) if len(components) == 0 { return } bc.ChildComponents = append(bc.ChildComponents, components...) return } // Remove 移除一个子组件 func (bc *BaseComponent) Remove(c IComponent) (err error) { if len(bc.ChildComponents) == 0 { return } for k, childComponent := range bc.ChildComponents { if c == childComponent { fmt.Println(runFuncName(), "移除:", reflect.TypeOf(childComponent)) bc.ChildComponents = append(bc.ChildComponents[:k], bc.ChildComponents[k+1:]...) } } return } // Do 执行组件&子组件 func (bc *BaseComponent) Do(ctx context.Context) (err error) { // Do nothing return } // ChildsDo 执行子组件 func (bc *BaseComponent) ChildsDo(ctx context.Context) (err error) { // 执行子组件 for _, childComponent := range bc.ChildComponents { if err = childComponent.Do(ctx); err != nil { return err } } return } type All struct { BaseComponent } type A1 struct { BaseComponent } func (a *A1) Do(ctx context.Context) (err error) { fmt.Println(runFuncName(), "A1...") a.ChildsDo(ctx) return } type A11 struct { BaseComponent } func (a *A11) Do(ctx context.Context) (err error) { fmt.Println(runFuncName(), "A11...") a.ChildsDo(ctx) return } type A12 struct { BaseComponent } func (a *A12) Do(ctx context.Context) (err error) { fmt.Println(runFuncName(), "A12...") a.ChildsDo(ctx) return } type B1 struct { BaseComponent } func (a *B1) Do(ctx context.Context) (err error) { fmt.Println(runFuncName(), "B1...") a.ChildsDo(ctx) return } type C1 struct { BaseComponent } func (a *C1) Do(ctx context.Context) (err error) { fmt.Println(runFuncName(), "C1...") a.ChildsDo(ctx) fmt.Printf("%+v\n", ctx.Value("a")) return } func main() { // 初始化订单结算页面 这个大组件 all :=&All{} a1 := &A1{} a1.Mount(&A11{}, &A12{}) b1 := &B1{} all.Mount(a1, b1,&C1{}) // 只能删除本身的子组件 all.Remove(b1) // 开始构建页面组件数据 ctx := context.WithValue(context.Background(), "a", "b") all.ChildsDo(ctx) } // 获取正在运行的函数名 func runFuncName() string { pc := make([]uintptr, 1) runtime.Callers(2, pc) f := runtime.FuncForPC(pc[0]) return f.Name() } ``` </details> <br/> ### 策略模式 优点: 1. 提供了对"开闭原则"的完美支持 2. 避免使用多条件的转移语句 3. 提供了管理相关算法族的办法 缺点: 1. 客户端必须知道所有的策略类 2. 策略模式将造成产生很多策略类 适合场景: 1. 需要动态地在几种算法中选择一种 2. 多个类区别仅在于它们的行为或算法不同的场景 简单demo: <details> <summary>main.go</summary> ``` package main import "log" // 实现一个上下文的类 type Context struct { Strategy } // 抽象的策略 type Strategy interface { Do() } type Strategy1 struct { } func (s *Strategy1) Do() { log.Printf("%v","实现策略1") } type Strategy2 struct { } func (s *Strategy2) Do() { log.Printf("%v","实现策略2") } func main() { context := Context{} context.Strategy=&Strategy1{} context.Do() strategy2 :=&Strategy2{} context.Strategy=strategy2 context.Do() /** 2020/05/12 07:29:56 实现策略1 2020/05/12 07:29:56 实现策略2 */ } ``` </details> <br /> 实战模拟: 实现一个日志记录器满足:文件记录和数据库记录2种方式 <details> <summary>详情</summary> ``` package main import "log" type LogManager struct { Logging } func NewLogManager(logging Logging) *LogManager { return &LogManager{Logging: logging} } type Logging interface { Info() Error() } type FileLogging struct{ } func (f *FileLogging) Info() { log.Printf("%v","文件记录 info") } func (f *FileLogging) Error() { log.Printf("%v","文件记录 Error") } type DbLogging struct{ } func (f *DbLogging) Info() { log.Printf("%v","数据库记录 info") } func (f *DbLogging) Error() { log.Printf("%v","数据库记录 Error") } func main() { filelog := &FileLogging{} manager := NewLogManager(filelog) manager.Info() manager.Error() dblog:=&DbLogging{} manager.Logging=dblog manager.Info() manager.Error() } ``` </details> <br /> ### 遍历策略模式 <details> <summary>main.go</summary> ``` package main import "log" type LogManager struct { Log []Logging } func NewLogManager(logging ...Logging) *LogManager { return &LogManager{ Log: logging, } } func (l *LogManager) Info() { for _, logging := range l.Log { logging.Info() } } func (l *LogManager) Error() { for _, logging := range l.Log { logging.Error() } } type Logging interface { Info() Error() } type FileLogging struct{ } func (f *FileLogging) Info() { log.Printf("%v","文件记录 info") } func (f *FileLogging) Error() { log.Printf("%v","文件记录 Error") } type DbLogging struct{ } func (f *DbLogging) Info() { log.Printf("%v","数据库记录 info") } func (f *DbLogging) Error() { log.Printf("%v","数据库记录 Error") } func main() { filelog := &FileLogging{} dblog:=&DbLogging{} manager := NewLogManager(filelog,dblog) manager.Info() manager.Error() } ``` </details> <br />