关于后端:一文学会常见设计模式

11次阅读

共计 18427 个字符,预计需要花费 47 分钟才能阅读完成。

博客:cbb777.fun

全平台账号: 安妮的心动录

github: https://github.com/anneheartrecord

下文中我说的可能对,也可能不对,鉴于笔者程度无限,请君自辨。有问题欢送大家找我探讨

实践

设计模式从何而来

模式:每个模式都形容了一个在咱们的环境中一直呈现的问题,而后形容了该问题的解决方案的外围,也就是说,设计模式是在特定环境下人们解决某类反复呈现问题的一套胜利或者无效的解决方案

软件设计模式

Gang of Four 提出了软件设计模式
Gof 提出的设计模式有 23 个,包含

  • 创立型模式:如何创建对象
  • 结构型模式:如何实现类或者对象的组合
  • 行为型模式:类或者对象怎么交互以及怎么调配职责

“简略工厂模式”不属于 23 种
设计模式:GOF 的 23 种 + 简略工厂模式

设计准则

设计准则是设计模式的核心思想,一共有七种

  • 繁多职责准则:类的职责繁多,对外只提供一种性能,而引起类变动的起因都应该只有一个
  • 开闭准则 :类的改变 是通过减少代码 进行的,而不是批改源代码
  • 里氏代换准则:任何抽象类(interface 接口)呈现的中央都能够用他的实现类进行替换,理论就是虚构机制,语言级别实现面向对象性能
  • 依赖倒转准则 :依赖于形象(接口),不要依赖具体的实现(类),也就是 针对接口 编程
  • 接口隔离准则:不应该强制用户的程序依赖他们不须要的接口办法。一个接口应该只提供一种对外性能,不应该把所有操作都封装到一个接口中去
  • 合成复用准则:如果应用继承,会导致父类的任何变换都可能影响到子类的行为。如果应用对象组合,就升高了这种依赖关系。对于继承和组合,优先应用组合
  • 迪米特法令 一个对象该当对其余对象尽可能少的理解,从而升高各个对象之间的耦合,进步零碎的可维护性,例如在一个程序中,各个模块互相调用时,通常会提供一个同一的接口来实现。这样其余模块不须要理解另外一个模块的外部实现细节,这样当一个模块外部的实现产生扭转的时候,不会影响其余模块的应用(黑盒原理)

    繁多职责准则

    类的职责是繁多的,对外只提供一种性能,而引起类变动的起因也应该只有一个
    在面向对象编程的过程中,设计一个类,倡议对外提供的性能繁多,接口繁多,影响一个类的范畴就限定在这一个接口上,一个类的一个接口具备这个类的性能含意,职责繁多不简单
    一个类对外只提供一种性能
    实例代码

    package main
    
    import "fmt"
    
    // 以下代码不遵循繁多职责准则,一个类实现了多个性能
    // 即一个 clothes 构造体实现了“工作打扮”和“逛街打扮”两个办法
    type Clothes struct {
    }
    
    //func (c *Clothes) Style() {//    fmt.Println("工作的打扮")
    //}
    //
    //func (c *Clothes) Style2() {//    fmt.Println("逛街的打扮")
    //}
    //
    //func main() {//    c := Clothes{}
    //    c.Style()
    //    c.Style2()
    //}
    
    // 繁多职责准则
    // 每一个类(构造体)负责一个性能或者一个逻辑
    
    type ClothesShop struct {
    }
    
    type ClothesWork struct {
    }
    
    func (c *ClothesWork) Style() {fmt.Println("工作的打扮")
    }
    
    func (c *ClothesShop) Style() {fmt.Println("逛街的打扮")
    }
    
    func main() {c := ClothesWork{}
      c.Style()
      c1 := ClothesShop{}
      c1.Style()}
    

    开闭准则

    开闭准则的核心思想就是当咱们增加一个新性能的时候,不是通过批改代码,而是通过削减代码来实现的。
    如果咱们应用接口 interface 就能够进行一层形象,而后提供一个形象的办法供业务进行实现。
    减少性能的时候去减少代码而不是批改代码
    示例代码

    // 以下代码是平铺式设计 每当增加一个业务就须要减少办法 会导致 Banker 类越来越臃肿
    // 不合乎开闭准则  每当有新的性能呈现就要对类增加对应性能的代码
    // 当 Banker 业务越多再批改 Banker 的业务或者增加新业务的时候 呈现问题的问题也会越来越大
    // 耦合度太高 Banker 的职责也不够繁多 代码的保护老本与业务的复杂程度成正比
    
    //type Banker struct {//}
    //
    //func (b *Banker) Save() {// fmt.Println("进行了 贷款业务...")
    //}
    //
    //func (b *Banker) Transfer() {// fmt.Println("进行了 转账业务...")
    //}
    //
    //func (b *Banker) Pay() {// fmt.Println("进行了 领取业务...")
    //}
    // 新增的 Deal 服务
    //func (b *Banker) Deal() {// fmt.Println("进行了 交易业务...")
    //}
    //
    //func main() {// banker := &Banker{}
    // banker.Save()
    // banker.Transfer()
    // banker.Pay()
    //}
    
    // 开闭准则
    // 在 Go 中的形容就是通过接口实现多态,每个类去实现接口
    // 这样的话就能实现一个后果:类的改变是通过减少代码进行的,而不是批改源代码
    
    type AbstractBanker interface {Business()
    }
    
    type SaveBanker struct {
    }
    
    func (sb *SaveBanker) Business() {fmt.Println("进行了贷款")
    }
    
    // 增加转账性能
    
    type TransferBanker struct {
    }
    
    func (tb *TransferBanker) Business() {fmt.Println("进行了转账")
    }
    
    // 能够基于形象层进行业务封装 - 针对 interface 接口进行封装
    
    func BankBusiness(banker AbstractBanker) {banker.Business()
    }
    
    func main() {sb := SaveBanker{}
     sb.Business()
     tb := TransferBanker{}
     tb.Business()
     BankBusiness(&sb)
     BankBusiness(&tb)
    }

    依赖倒转准则

    在设计一个零碎的时候咱们能够将模块分成三个档次,形象层、实现层、业务逻辑层。咱们首先将形象层的模块和接口定义进去,而后通过 interface 接口的设计按照形象层顺次实现每个实现层的模块,在咱们写实现层代码的时候,实际上只须要参考对应的形象层,实现每个模块。而业务逻辑层也是通过形象层裸露进去的接口进行实现的,能够应用的办法就是形象层裸露进去的办法
    模块与模块之间依赖形象而不是具体实现

    // 上面的代码耦合度很高,不满足依赖倒转准则
    // 如果要满足张三开宝马,李四开飞驰,就须要从新增加代码
    // 如果司机人数为 m,汽车数量为 n,那么须要编写的办法为 m *n
    
    //type Benz struct {//}
    //
    //func (b *Benz) Run() {//    fmt.Println("Benz is running")
    //}
    //
    //type BMW struct {//}
    //
    //func (b *BMW) Run() {//    fmt.Println("BMW is runnning")
    //}
    //
    //type Zhang3 struct {//}
    //
    //func (z *Zhang3) DriveBenz(benz *Benz) {//    benz.Run()
    //    fmt.Println("Zhang3 is driving Benz")
    //}
    //
    //type Li4 struct {//}
    //
    //func (l *Li4) DriveBMW(bmw *BMW) {//    bmw.Run()
    //    fmt.Println("Li4 is driving BMW")
    //}
    //
    //func main() {//    benz := &Benz{}
    //    zhang3 := Zhang3{}
    //    zhang3.DriveBenz(benz)
    //    bmw := &BMW{}
    //    li4 := Li4{}
    //    li4.DriveBMW(bmw)
    //}
    
    // 形象层
    
    type Car interface {Run()
    }
    
    type Driver interface {Driver(car Car)
    }
    
    // 实现层
    // 每个车子都实现 Run 办法
    // 每个司机都实现 Drive 办法
    // 这样须要实现的办法为 m +n 
    // 而且实现层只依赖于形象层
    type Benz struct {
    }
    
    func (b *Benz) Run() {fmt.Println("Benz is Running")
    }
    
    type BMW struct {
    }
    
    func (b *BMW) Run() {fmt.Println("BMW is Running")
    }
    
    type Zhang3 struct {
    }
    
    func (z3 *Zhang3) Drive(car Car) {fmt.Println("zhang3 drive car")
      car.Run()}
    
    type Li4 struct {
    }
    
    func (l4 *Li4) Drive(car Car) {fmt.Println("li4 drive car")
      car.Run()}
    
    // 业务逻辑层
    
    func main() {var benz Car = new(Benz)
      z := new(Zhang3)
      z.Drive(benz)
    }

合成复用准则

如果应用继承,会导致父类的任何变换都可能影响到子类的行为。如果应用对象组合,就升高了这种依赖关系。对于继承和组合,优先应用组合。
应用组合来实现父类办法

type Cat struct {
}

func (c *Cat) Eat() {fmt.Println("小猫吃饭")
}

// 应用继承来实现 增加一个睡觉的办法

type CatB struct {Cat}

func (c *CatB) Sleep() {fmt.Println("小猫睡觉")
}

// 应用组合来增加可睡觉的办法
 
type CatC struct {C *Cat}

func (cc *CatC) Sleep() {fmt.Println("小猫睡觉")
}
func main() {c := &Cat{}
    c.Eat()
    cb := &CatB{}
    cb.Eat()
    cb.Sleep()
    cc := &CatC{}
    cc.Sleep()}

迪米特法令

依赖第三方进行解耦

接口的意义

接口的意义就是实现多态的思维,咱们能够依据 interface 类型来设计 API 接口,那么这种 API 接口的适应能力不仅可能适应当下所实现的全副模块,也适应将来实现的模块来进行调用。** 调用将来 **兴许是接口最大的意义所在,良好的架构师能够针对 interface 进行设计一套框架,在将来的许多年后依然能够实用

设计模式

创立型模式

思维就是能够通过增加一层 工厂模块,来做到业务逻辑层和根底模块层之间的耦合,防止业务逻辑层对根底模块层的间接依赖。

简略工厂模式

简略工厂模式并不属于 GoF 的 23 种设计模式,它是开发者自发认为的一种十分繁难的设计模式,其角色和职责如下:

  • 工厂:简略工厂模式的外围,它负责创立所有实例的外部逻辑。工厂类能够被外界间接调用,创立所须要的产品对象
  • 形象产品:简略工厂模式所创立的所有对象的分类,它负责形容实例所私有的公共接口
  • 具体产品:简略工厂模式所创立的具体实例对象

设计模式类图

// 形象层

type Fruit interface {Show()
}

// 实现层

type Apple struct {Fruit}

func (a *Apple) Show() {fmt.Println("i am apple")
}

type Banana struct {Fruit}

func (b *Banana) Show() {fmt.Println("i am banana")
}

type Pear struct {Fruit}

func (p *Pear) Show() {fmt.Println("i am pear")
}

// 工厂模块

type Factory struct {
}

func (f *Factory) CreateFruit(kind string) Fruit {
    var fruit Fruit
    if kind == "apple" {fruit = new(Apple)
    } else if kind == "banana" {fruit = new(Banana)
    } else if kind == "pear" {fruit = new(Pear)
    }
    return fruit
}

// 逻辑层

func main() {factory := new(Factory)
    apple := factory.CreateFruit("apple")
    apple.Show()
    banana := factory.CreateFruit("banana")
    banana.Show()}

优缺点
长处:

  • 实现了对象创立和应用的拆散
  • 不须要记住具体类名,记住参数就能够,缩小使用者记忆量

毛病:

  • 对工厂职责过重,一旦不能工作,零碎会受到影响
  • 减少零碎中类的个数,复杂度和了解度减少
  • 违反“开闭准则”,增加新产品须要批改工厂逻辑,工厂越来越简单

实用场景

  • 工厂类负责创立的对象比拟少,因为创立的对象较少,不会造成工厂办法中的逻辑太简单
  • 客户端只晓得传入工厂类的参数,对于如何创建对象并不关怀

工厂办法模式

  • 形象工厂:工厂的外围,任何工厂类都必须实现这个接口
  • 工厂:具体工厂是形象工厂的一个实现,负责实例化产品对象
  • 形象产品:工厂办法模式所创立的所有对象的父类,它负责形容所有实例所共有的公共接口
  • 具体产品:工厂办法模式所创立的具体实例对象

简略工厂 + 开闭准则 = 工厂
模式类图

// 形象层

type fruit interface {show()
}

// 工厂类(形象的接口)type AbstractFactory interface {CreateFruit() fruit
}

// 根底模块层

type apple struct {fruit}

func (a *apple) show() {fmt.Println("i am apple")
}

type banana struct {fruit}

func (b *banana) show() {fmt.Println("i am banana")
}

type pear struct {fruit}

func (p *pear) show() {fmt.Println("i am pear")
}

type AppleFactory struct {AbstractFactory}

func (fac *AppleFactory) CreateFruit() fruit {
    var f fruit
    f = new(apple)
    return f
}

type BananaFactory struct {AbstractFactory}

func (fac *BananaFactory) CreateFruit() fruit {
    var f fruit
    f = new(banana)
    return f
}

type PearFactory struct {AbstractFactory}

func (fac *PearFactory) CreateFruit() fruit {
    var f fruit
    f = new(pear)
    return f
}

// 业务逻辑层
func main() {
    // 需要 1 须要一个具体的苹果对象
    // 须要一个具体的苹果工厂
    var a AbstractFactory
    a = new(AppleFactory)
    // 生产一个具体的水果
    var apple fruit
    apple = a.CreateFruit()
    apple.show()}

优缺点
长处:

  • 不须要记住具体类名,甚至连具体参数都不必记忆
  • 实现了对象创立和应用的拆散
  • 零碎的可拓展性也变得十分好,不须要批改接口和原类
  • 对于新产品的创立,合乎开闭准则

毛病:

  • 减少零碎中的类的个数,复杂度和了解度减少
  • 减少了零碎的抽象性

实用场景

  1. 客户端不晓得它所须要的对象的类
  2. 形象工厂类通过其子类来指定创立哪个对象

    形象工厂模式

    工厂模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会减少零碎的开销。因而,能够将一些相干的产品组成一个“产品族”,从而由同一个工厂来对立生产。

  • 形象工厂:它申明了一组用于创立一组产品的办法,每一个办法对应一种产品
  • 具体工厂:它实现了在形象工厂中申明的创立产品的办法,生成一组具体产品,这些产品形成了一个产品族,每一个产品都位于某在产品等级构造中
  • 形象产品:它为每种产品申明接口,在形象产品中申明了产品所具备的业务办法
  • 具体产品:它定义具体工厂生产的具体产品对象,实现形象产品接口中申明的业务办法

模式例图


// 形象层

type AbstractApple interface {ShowApple()
}

type AbstractBanana interface {ShowBanana()
}

type AbstractPear interface {ShowPear()
}

// 形象的工厂

type AbstractFactory interface {CreateApple() AbstractApple
    CreateBanana() AbstractBanana
    CreatePear() AbstractPear}

// 实现层

type ChinaApple struct {
}

type ChinaBanana struct {
}

type ChinaPear struct {
}

type ChinaFactory struct {
}

func (ca *ChinaApple) ShowApple() {fmt.Println("china apple")
}

func (cb *ChinaBanana) ShowBanana() {fmt.Println("china banana")
}

func (cp *ChinaPear) ShowPear() {fmt.Println("china pear")
}

func (cf *ChinaFactory) CreateApple() AbstractApple {
    var apple AbstractApple
    apple = new(ChinaApple)
    return apple
}

func (cf *ChinaFactory) CreateBanana() AbstractBanana {
    var b AbstractBanana
    b = new(ChinaBanana)
    return b
}

func (cf *ChinaFactory) CreatePear() AbstractPear {
    var p AbstractPear
    p = new(ChinaPear)
    return p
}

func main() {
    // 须要中国的水果
    //1. 创立中国工厂
    var cF AbstractFactory
    cF = new(ChinaFactory)
    var cApple AbstractApple
    cApple = cF.CreateApple()
    cApple.ShowApple()
    var cBanana AbstractBanana
    cBanana = cF.CreateBanana()
    cBanana.ShowBanana()
    var cPear AbstractPear
    cPear = cF.CreatePear()
    cPear.ShowPear()}

优缺点
长处

  1. 用于工厂办法模式的长处
  2. 当一个产品族中的多个对象被设计成一起工作时,它可能保障客户端始终只应用同一个产品族中的对象
  3. 减少新的产品族很不便,毋庸批改已有零碎,合乎“开闭准则”

毛病

  1. 减少新的产品等级构造麻烦,须要对原有零碎进行较大的批改,甚至须要批改形象层代码,这显然会带来较大的不便,违反了”开闭准则“

实用场景

  1. 零碎中有多于一个的产品族,而每次只应用其中某一产品族,能够通过配置文件等形式来使得用户能够动静扭转产品族,也能够很不便地减少新的产品族
  2. 产品等级构造稳固。设计实现之后,不会像零碎中减少新的产品等级构造或者删除已有的产品等级构造

    三种工厂的区别

  • 简略工厂:一个工厂负责创立所有产品,违反开闭准则,增加新产品须要批改工厂逻辑,工厂会变得越来越简单
  • 工厂:一个工厂创立一个产品,零碎的可扩展性十分好,无需批改接口和类,然而零碎中类的个数变多,复杂度和了解度减少
  • 形象工厂:一个工厂创立一系列(同一个产品族)的产品,减少新的产品族很不便,无需批改已有零碎,合乎开闭准则,减少新的产品等级构造很麻烦,须要对原有零碎进行较大的批改,违反了开闭准则,相当于在工厂办法的模式下进行了折中,如果产品构造等级稳固,那么就相当于齐全遵循开闭

    单例模式

    保障一个类、只有一个实例存在,同时提供能对该实例加以拜访的全局拜访办法。
    要解决的问题是:保障一个类永远只能有一个对象,且该对象的性能仍然能被其余模块应用。
    类图:

    
    // 三个要点
    // 某个类只能有一个实例
    // 它必须自行创立这个实例
    // 必须自行向整个零碎提供这个实例
    
    // 总结:一个类永远只能有一个对象,这个对象还能被零碎的其余模块应用
    
    //1. 因为这个类必须保障私有化 所以首字母要小写
    type singelton struct{}
    
    // 2. 指针只能指向这个惟一对象,然而这个指针不能改变方向,也必须小写
    var instance *singelton = new(singelton)
    
    // 3. 对外提供一个办法来获取到这个对象 把 instance 的写权限去掉 只裸露读权限
    
    func GetInstance() *singelton {return instance}
    
    func (s *singelton) DoSomeThing() {fmt.Println("Do something")
    }
    
    func main() {s := GetInstance()
      s.DoSomeThing()}
    
    // 懒汉式的单例模式: 只有被第一次拜访的时候 才给 instance 赋值 平时为 nil
    // 然而懒汉式可能有并发问题:同时有两个 Getinstance 同一时刻首次调用 那么就会呈现两个 instance 能够加锁解决
    // 锁的粒度太大了 能够通过一个 uint 的标记 应用 atomic.LoadUnit 函数判断 不必每次拜访都加锁
    // 或者间接应用 sync.Once 进行 new 这是对 atomic.LoadUint 的封装
    

优缺点
长处:

  1. 单例模式提供了对惟一实例的受控拜访
  2. 节约系统资源,因为在零碎内存中只存在一个对象

毛病:

  1. 扩展性差,单利模式中没有形象层
  2. 单例类的职责过重

实用场景

  1. 零碎只须要一个实例对象,比方零碎要求提供一个惟一的序列号生成器或者资源管理器,或者须要思考资源耗费太大而只容许创立一个对象
  2. 客户调用类的单个实力只容许应用一个公共拜访点,除了该节点之外,不能通过其余路径拜访该实例

    结构型模式

    代理模式

    Proxy 模式又叫代理模式,能够为其余对象提供一种代理(Proxy)以管制对这个对象的拜访。
    所谓代理,是指具备与代理元(被代理的对象)具备雷同的接口的类,客户端必须通过代理与被代理的指标类进行交互

  • 形象主题:实在主题与代理主题的独特接口
  • 实在主题:定义了代理角色所代表的实在对象
  • 代理主题角色:含有对实在主题角色的援用,代理角色通常在客户端调用给实在主题对象之前或者之后执行某些操作
type Goods struct {
    Kind string
    Fact bool
}

// 形象层
type Shopping interface{Buy(goods *Goods)
}

// 实现层
type KoreaShopping struct {
}

func (ks *KoreaShopping) Buy(good *Goods) {fmt.Println("go korea buy", good.Kind)
}

type AmericaShopping struct {
}

func (as *AmericaShopping) Buy(good *Goods) {fmt.Println("go america buy", good.Kind)
}

type AfricaShopping struct {
}

func (as *AfricaShopping) Buy(good *Goods) {fmt.Println("go africa buy", good.Kind)
}

// 海内代理

type OverSeasProxy struct {shopping Shopping}

func (op *OverSeasProxy) Buy(good *Goods) {
    //1. 分别真伪
    if op.distinguish(good) == true {op.shopping.Buy(good)
        op.check(good)
    }
    //2. 调用具体须要被代理的 Buy 办法
    //3. 海关安检
}

// 分别真伪
func (op *OverSeasProxy) distinguish(goods *Goods) bool {fmt.Println("对", goods.Kind, "进行了分别真伪")
    if goods.Fact == false {fmt.Println("发现假货")
    }
    return goods.Fact
}

func (op *OverSeasProxy) check(good *Goods) {fmt.Println("通过海关")
}

func NewProxy(s Shopping) Shopping {return &OverSeasProxy{s}
}

func main() {
    g1 := Goods{
        Kind: "韩国面膜",
        Fact: true,
    }
    g2 := Goods{
        Kind: "苹果",
        Fact: false,
    }
    var k Shopping = new(KoreaShopping)
    var p Shopping
    p = NewProxy(k)
    p.Buy(&g1)
    var a Shopping = new(AmericaShopping)
    p = NewProxy(a)
    p.Buy(&g2)
}

优缺点
长处:
1. 可能协调调用者和被调用者,在肯定水平上升高了零碎的耦合度
2. 客户端能够针对形象主题角色进行编程,合乎开闭准则,零碎具备姣好的灵活性和可拓展性
毛病:
1. 实现较为简单
实用场景
为其余对象提供一种代理以管制对这个对象的拜访

装璜模式

装璜模式(Decorator)用来动静地给一个对象减少一些额定的职责,比生成子类实现更加灵便
装璜模式关注于在一个对象上动静的增加办法,然而代理模式关注于管制对对象的拜访。换句话说,用代理模式,代理类能够对它的客户暗藏一个对象的具体信息。因而,当应用模式的时候,咱们经常在一个代理类中创立一个对象的实例。并且当咱们应用装璜器模式的时候,咱们通常的做法是将原始对象作为一个参数传给装璜者的结构器。

  • 形象构件:是具体构件和形象装璜类的独特父类,申明了在具体构件中实现的业务办法,它的引入能够使客户端以统一的办法解决未被装璜的对象以及装璜之后的对象,实现客户端的通明操作
  • 具体构件:它是形象构建类的子类,用于定义具体的构件对象,实现了在形象构件中申明的办法,装璜器能够给它减少额定的职责

例图

type Phone interface {Show()
}

// 形象的装璜器,装璜器的根底类

type Decorator struct {phone Phone}

func (d *Decorator) Show() {}

// 具体的构件

type Huawei struct {
}

func (hw *Huawei) Show() {fmt.Println("it is a huawei phone")
}

type Xiaomi struct {
}

func (xm *Xiaomi) Show() {fmt.Println("it is a xiaomi phone")
}

type MoDecorator struct {Decorator}

func (md *MoDecorator) Show() {md.phone.Show()
    fmt.Println("it is a mo phone")
}

func NewMoDecorator(p Phone) Phone {
    return &MoDecorator{Decorator{p,}}
}

type KeDecorator struct {Decorator}

func (kd *KeDecorator) Show() {kd.phone.Show()
    fmt.Println("it is a ke phone")
}

func NewKeDecorator(p Phone) Phone {return &KeDecorator{Decorator{p}}
}

func main() {
    var hw Phone
    hw = new(Huawei)
    hw.Show()
    var mo Phone
    mo = NewMoDecorator(hw)
    mo.Show()
    var ke Phone
    ke = NewKeDecorator(hw)
    ke.Show()}

优缺点
长处:

  1. 对于扩大一个对象的性能,装璜模式比继承更加灵便,不会导致类的个数急剧减少
  2. 能够通过一种动静的形式来扩大一个对象的性能,从而实现不同的行为
  3. 能够对一个对象进行屡次装璜
  4. 具体构建类与具体装璜类能够独立变动,合乎开闭

毛病:

  1. 应用装璜模式进行零碎设计时将产生很多小对象,大量小对象的产生势必会占用更多的系统资源,影响程序的性能
  2. 装璜模式提供了一种比继承更加灵便冲动的解决方案,同时意味着排错也比拟艰难

实用场景

  1. 动静、通明的形式给单个对象增加职责
  2. 当不能采纳继承的形式对系统进行拓展或者采纳继承不利于零碎拓展和保护时能够应用装璜模式

    装璜与代理的区别

    装璜器模式关注于在一个对象上动静的增加办法,然而代理模式关注于管制对对象的拜访。换句话说,用代理模式,代理类能够对它的客户暗藏一个对象的具体信息。

    适配器模式

    将一个类的接口转换成客户心愿的另外一个接口。使得本来因为接口不兼容而不能一起工作的那些类能够一起工作。

  • 指标抽象类:定义客户所需接口,能够是具体类也能够是形象接口
  • 适配器类:能够调用另一个接口,作为一个转换器,让指标抽象类和适配者类进行适配
  • 适配者类:被适配的角色,定义了一个曾经存在的接口,这个接口须要适配
type V5 interface {Use5V()
}
type V220 struct {
}

type phone struct {v V5}

func (v *V220) Use220V() {fmt.Println("应用 220V 的电压")
}

type Adapter struct {v220 *V220}

func (a *Adapter) Use5V() {fmt.Println("应用适配器,以 220V 的电压充电")
    a.v220.Use220V()}

func NewPhone(v V5) *phone {return &phone{v}
}

func NewAdapter(v220 *V220) *Adapter {return &Adapter{v220}
}

func (p *phone) Charge() {fmt.Println("Phone 进行了充电")
    p.v.Use5V()}

func main() {phone := NewPhone(NewAdapter(&V220{}))
    phone.Charge()}

优缺点
长处:

  1. 将指标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无需批改原有构造
  2. 减少了类的透明性和复用性,将具体的业务实现封装在适配者类中,对于客户端类而言是通明的,而且进步了适配者的复用性,同一个适配者类能够在多个不同的零碎中复用
  3. 灵活性和扩展性都很好,能够很不便地更换适配器,合乎开闭准则

毛病:
适配器中置换适配者类的某些办法比拟麻烦
适应场景
1. 零碎须要应用一些现有的类,而这些类的接口不合乎零碎的须要,甚至没有这些类的源代码
2. 想创立一个能够重复使用的类,用来与一些彼此之间没有太大关联的一些类,包含一些可能在未来引进的类一起工作

外观模式

外观模式(Facade),为一组具备类型性能的类群,比方类库,子系统等等,提供一个统一的简略的界面

  • 外观角色:为调用方,定义简略的调用接口
  • 子系统角色:性能提供方,指提供性能的类群

type SubSystemA struct {
}

type SubSystemB struct {
}

type SubSystemC struct {
}

type SubSystemD struct {
}

func (sa *SubSystemA) MethodA() {fmt.Println("sub method a")
}

func (sb *SubSystemB) MethodB() {fmt.Println("sub method b")
}

func (sc *SubSystemC) MethodC() {fmt.Println("sub method c")
}

func (sd *SubSystemD) MethodD() {fmt.Println("sub method d")
}

// 外观类
type Facade struct {
    a *SubSystemA
    b *SubSystemB
    c *SubSystemC
    d *SubSystemD
}

func (f *Facade) MethodOne() {f.a.MethodA()
    f.b.MethodB()}

func (f *Facade) MethodTwo() {f.c.MethodC()
    f.d.MethodD()}

func main() {f := Facade{}
    f.MethodOne()}

优缺点
长处:
1. 它对客户端屏蔽了子系统组件,缩小了客户端所需解决的对象数目,并使得子系统应用起来更加容易。通过引入外观模式,客户端代码将变得很简略,与之关联的对象也很少。
2. 它实现了子系统与客户端之间的松耦合关系,这使得子系统的变动不会影响到调用它的客户端,只须要调整外观类即可
3. 一个子系统的批改对其余子系统没有任何影响
毛病;
1. 不能很好的限度客户端间接应用子系统类,如果对客户端拜访子系统类做太多的限度则缩小了可变性和灵活性
2. 如果设计不当,减少新的子系统可能须要批改外观类的源代码,违反了开闭准则
实用场景

  1. 简单零碎须要简略入口应用
  2. 客户端程序与多个子系统之间存在很大的依赖性
  3. 在层次化构造中,能够应用外观模式定义零碎中每一层的入口,层与层之间不间接产生分割

    行为型模式

    模板办法模式

  • 抽象类:在抽象类中定义了一系列基本操作,能够是具体的,也能够是形象的,每一个基本操作对应算法的一个步骤,在其子类中能够重定义或者实现这些步骤
  • 具体子类:是抽象类的子类,用于实现在父类中申明的形象基本操作以实现子类特定算法的步骤,也能够笼罩在父类中曾经实现的具体操作
// 抽象类 做饮料

type Beverage interface {BoilWater()
    Brew()
    PourInCup()
    AddThings()}

// 封装一套流程模板
type template struct {b Beverage}

func (t *template) MakeBeverage() {
    if t == nil {return}
    t.b.BoilWater()
    t.b.Brew()
    t.b.PourInCup()
    t.b.AddThings()}

type MakeCoffee struct {template}

func (mc *MakeCoffee) BoilWater() {fmt.Println("boil the water")
}

func (mc *MakeCoffee) Brew() {fmt.Println("use boiled water to brew")
}

func (mc *MakeCoffee) PourInCup() {fmt.Println("pour the coffee to the cup")
}

func (mc *MakeCoffee) AddThings() {fmt.Println("add sugar")
}

func NewMakeCoffee() *MakeCoffee {m := new(MakeCoffee)
    m.b = m
    return m
}

func main() {makeCoffee := NewMakeCoffee()
    makeCoffee.MakeBeverage()}

优缺点
长处:
1. 在父类中形式化地定义一个算法,而由它的子类来实现细节的解决,在子类实现具体的解决算法时不会扭转算法中步骤的执行程序
2. 模板办法是一种代码复用技术,他提取了类库中的公共行为,将公共行为放在父类中
3. 可实现一种反向控制结构,通过子类笼罩父类的钩子办法来决定某一特定步骤是否须要执行
4. 不同的子类能够提供根本办法的不同实现,更换和减少新的子类很不便,合乎繁多职责准则和开闭准则
毛病:
须要为每一个根本办法的不同实现提供一个子类,如果父类中可变的根本办法太多,将会导致类的个数减少,零碎更加宏大,设计也更加形象。
实用场景
1. 具备同一的操作步骤或操作过程
2. 具备不同的操作细节
3. 存在多个具备同样操作步骤的利用场景,但某些具体的操作系统却各不相同
在抽象类中对立操作步骤,并规定好接口;让子类实现接口。这样能够把各个子类和操作步骤解耦合

命令模式

讲一个申请封装成一个对象,从而让咱们可用不同的申请对客户进行参数化;对申请排队或者记录申请日志,以及反对可撤销的操作。命令模式是一种对象行为型模式,其别名为动作模式。命令模式能够将申请发送者和接收者齐全解耦,发送者与接收者之间没有间接援用关系,发送申请的对象只须要晓得如何发送申请,而不比晓得如何实现申请。

  • 形象命令类:一个抽象类或者接口,通过这些办法能够调用申请接收者的相干操作
  • 具体命令类:具体命令类是形象命令类的子类,实现了在形象命令类中申明的办法,它对应具体的接收者对象,将接收者对象的动作绑定其中
  • 调用者:申请发送者,通过命令对象来执行申请
  • 接收者:接收者执行与申请相干的操作

type Doctor struct {
}

func (d *Doctor) treatEye() {fmt.Println("doctor treat eye")
}

func (d *Doctor) treatMouth() {fmt.Println("doctor treat mouth")
}

type Command interface {Treat()
}

type CommandTreatEye struct {d *Doctor}

func (cmd *CommandTreatEye) Treat() {cmd.d.treatEye()
}

type CommandTreatMouth struct {d *Doctor}

func (cmd *CommandTreatMouth) Treat() {cmd.d.treatMouth()
}

type Nurse struct {CmdList []Command
}

func (n *Nurse) Notify() {if len(n.CmdList) == 0 {return}
    for _, cmd := range n.CmdList {cmd.Treat()
    }
}

func main() {doctor := new(Doctor)
    cmdEye := CommandTreatEye{doctor}
    cmdMouth := CommandTreatMouth{doctor}
    nurse := new(Nurse)
    nurse.CmdList = append(nurse.CmdList, &cmdEye)
    nurse.CmdList = append(nurse.CmdList, &cmdMouth)
    nurse.Notify()}

优缺点
长处:
1. 升高零碎的耦合度
2. 新的命令很容易增加到零碎中,满足开闭准则
3. 能够比拟容易地设计一个命令队列或者宏命令
毛病:
应用命令模式可能会导致某些零碎有过多的具体命令类,因为针对每一个对申请接收者的调用操作都须要设计一个具体命令类,因而在某些零碎中可能须要提供大量的具体命令类,这将影响命令模式的应用
实用场景
1. 零碎须要将申请调用者和申请接收者解耦,使得调用者和接收者不间接交互。申请调用者无需晓得接收者的存在,兴许无晓得接收者是谁
2. 零碎须要在不同的工夫指定申请、将申请排队和执行申请
3. 零碎须要将一组操作组合在一起造成宏命令

策略模式

  • 环境类:环境类是应用算法的角色,它在解决某个问题时能够采纳多种策略,在环境类中维持一个对形象策略类的援用实例,用于定义所采纳的策略
  • 形象策略类:它为所反对的算法申明了形象办法,是所有策略类的父类,能够是抽象类或者具体类,也能够是接口
  • 具体策略类:它实现了在形象策略类中申明的算法,在运行时,具体策略类将笼罩在环境类中定义的形象策略类,应用一种具体的算法实现某个业务解决

例图

type WeaponStrategy interface {UseWeapon()
}

type AK47 struct {
}

func (ak *AK47) UseWeapon() {fmt.Println("应用 AK47 战斗")
}

type Knife struct {
}

func (k *Knife) UseWeapon() {fmt.Println("应用匕首战斗")
}

type Hero struct {strategy WeaponStrategy}

func (h *Hero) SetWeaponStrategy(s WeaponStrategy) {h.strategy = s}

func (h *Hero) Fight() {h.strategy.UseWeapon()
}

func main() {hero := Hero{}
    hero.SetWeaponStrategy(new(AK47))
    hero.Fight()
    hero.SetWeaponStrategy(new(Knife))
    hero.Fight()}

优缺点
长处:
1. 策略模式提供了对开闭准则的完满反对,用户能够在不批改原有零碎的根底上抉择算法或者行为
2. 应用策略模式能够防止多重条件抉择语句
3. 策略模式提供了一种算法的复用机制
毛病:
1. 客户端必须晓得所有的策略类,并自行决定应用哪一个策略类
2. 策略模式将造成零碎产生很多具体策略类,任何细小的变动都将导致系统要减少一个新的具体策略类
实用场景
筹备一组算法,并将每一个算法封装起来,使得它们能够调换

观察者模式

观察者模式是用于建设一种对象与对象之间的依赖关系,一个对象产生扭转时将主动告诉其余对象,其余对象将相应作出翻译。在观察者模式中,产生扭转的对象称为察看指标,而被告诉的对象称为观察者,一个察看指标能够对应多个观察者,而且这些观察者之间能够没有任何互相分割,能够依据须要减少和删除观察者,使得零碎更易于扩大。

  • 形象主题:被察看的对象
  • 具体主题:被观察者的具体实现
  • 观察者:接口或者抽象类
  • 具体观察者:观察者的具体实现
type Listener interface {OnTeacherComing()
}

type Notifier interface {AddListener(l Listener)
    RemoveListener(l Listener)
    Notify()}

type StuZhang3 struct {Badthing string}

func (s *StuZhang3) OnTeacherComing() {fmt.Println("zhang3 stop", s.Badthing)
}

type StuZhao4 struct {Badthing string}

func (s *StuZhao4) OnTeacherComing() {fmt.Println("zhao4 stop", s.Badthing)
}

type StuWang5 struct {Badthing string}

func (s *StuWang5) OnTeacherComing() {fmt.Println("wang5 stop", s.Badthing)
}

type ClassMonitor struct {listenerList []Listener
}

func (m *ClassMonitor) AddListener(l Listener) {m.listenerList = append(m.listenerList, l)
}

func (m *ClassMonitor) RemoveListener(l Listener) {
    for index, li := range m.listenerList {
        if li == l {m.listenerList = append(m.listenerList[:index], m.listenerList[index+1:]...)
            break
        }
    }
}

func (m *ClassMonitor) Notify() {
    for _, listener := range m.listenerList {listener.OnTeacherComing()
    }
}

func main() {
    s1 := &StuZhang3{Badthing: "抄作业",}
    s2 := &StuZhao4{Badthing: "玩手机",}
    s3 := &StuWang5{Badthing: "看他人玩手机",}
    classMonitor := new(ClassMonitor)
    classMonitor.AddListener(s1)
    classMonitor.AddListener(s2)
    classMonitor.AddListener(s3)
    classMonitor.Notify()}

优缺点
长处

  1. 观察者模式能够实现体现层和数据逻辑层的拆散,定义了稳固的音讯更新传递机制,并形象了更新接口,使得能够有各种各样不同的表示层充当具体观察者角色
  2. 观察者模式在察看指标和观察者之间建设了一个形象的耦合。察看指标只须要维持一个形象观察者的汇合,不须要理解其具体的观察者
  3. 观察者模式反对播送通信,察看指标会向所有已注册的观察者对象发送告诉,简化了一对多零碎设计的难度
  4. 观察者模式满足开闭准则

毛病

  1. 如果一个观察者对象有很多间接和间接的观察者,将所有的观察者都告诉到会破费很多工夫
  2. 如果在观察者和察看指标之间存在循环依赖,零碎可能会产生解体
  3. 观察者模式没有响应的机制让观察者晓得所察看到的对象是怎么发生变化的

实用场景

  1. 一个形象模型有两个方面,其中一个方面依赖于另一方面,将这两个方面封装在独立的对象中使得它们能够各自独立地扭转和复用
  2. 一个对象的扭转将导致一个或者多个其余对象也产生扭转
  3. 须要在零碎中创立一个触发链路,A 对象的行为影响 B,B 对象的行为影响 C

本文由 mdnice 多平台公布

正文完
 0