Go实现筹划模式

打印 上一主题 下一主题

主题 854|帖子 854|积分 2562

1、是什么

   筹划模式(Design Pattern)是一套被反复使用、多数人知晓的、颠末分类编目的、代码筹划履历的总结,使用筹划模式是为了可重用代码、让代码更轻易被他人明确并且包管代码可靠性。
  普通来说:是一个项目的代码层面筹划架构,代码功能的排版。相当于模板。
2、作用



  • 筹划模式提供了一套通用的筹划词汇和一种通用的形式来方便开发人员之间沟通和交换,使得筹划方案更加普通易懂
  • 筹划模式加强了系统的可重用性、可扩展性、可维护性
  • 提高代码的易读性,有助于别人更快地明确系统
3、分类

整体来看,筹划模式包含了如下 22 种,重要分为三大类

  • 创造型
  • 结构型
  • 行为型

4、常用筹划模式的思想



  • 创建型模式
筹划模式使用场景单例模式全局共享一个实例,且只需要被初始化一次的场景工厂模式简朴工厂模式:传入参数并返回一个结构体的实例;
抽象工厂模式:返回一个接口,通过返回接口,在不公开内部实现的环境下,让调用者使用你提供的各种功能。
工厂方法模式:将对象创建从由一个对象负责所有具体类的实例化,酿成由一群子类来负责对具体类的实例化,从而将过程解耦

  • 行为型模式
筹划模式使用场景策略模式需要接纳不同策略的场景模板模式需要在不改变算法框架的环境下,改变算法实行结果的场景

  • 结构型模式
筹划模式使用场景代理模式需要一个替人大概占位符,以控制对这个对象的访问的场景选项模式结构体参数很多,盼望创建一个携带默认值的结构体变量,并选择性修改此中一些参数的值;
结构体参数经常变动,变动时又不想修改创建实例的函数 5、筹划模式使用

创建型模式

创建型模式是处理对象创建的筹划模式,试图根据实际环境使用合适的方式创建对象,增加已有代码的机动性和可复用性。
工厂方法模式 Factory Method

  1. //假设我们的业务需要一个支付渠道,我们开发了一个Pay方法,其可以用于支付
  2. type Pay interface {
  3.   Pay() string
  4. }
  5. type PayReq struct {
  6.   OrderId string // 订单号
  7. }
  8. func (p *PayReq) Pay() string {
  9.   fmt.Println(p.OrderId)
  10.   return "支付成功"
  11. }
复制代码
  1. 如果业务需求变更,需要我们提供多种支付方式,一种叫APay,一种叫BPay,这二种支付方式所需的参数不同,APay只需要订单号OrderId,BPay则需要订单号OrderId和Uid。
复制代码
假如仅仅是在原有的代码基础上修改
  1. type Pay interface {
  2.   APay() string
  3.   BPay() string
  4. }
  5. type PayReq struct {
  6.   OrderId string // 订单号
  7.   Uid int64
  8. }
  9. func (p *PayReq) APay() string {
  10.   fmt.Println(p.OrderId)
  11.   return "APay支付成功"
  12. }
  13. func (p *PayReq) BPay() string {
  14.   fmt.Println(p.OrderId)
  15.   fmt.Println(p.Uid)
  16.   return "BPay支付成功"
  17. }
复制代码
我们为Pay接口实现了APay() 和BPay() 方法。虽然临时实现了业务需求,但却使得结构体PayReq变得冗余了,APay() 并不需要Uid参数。假如之后再增加CPay......代码会变得越来越难以维护
  1. //解决:
  2. //不再为接口提供APay、BPay方法,只提供Pay方法,并将A支付方式和B支付方式的区别下放到子类
  3. package factorymethod
  4. import "fmt"
  5. type Pay interface {
  6.   Pay(string) int
  7. }
  8. type PayReq struct {
  9.   OrderId string
  10. }
  11. type APayReq struct {
  12.   PayReq
  13. }
  14. func (p *APayReq) Pay() string {
  15.   fmt.Println(p.OrderId)
  16.   return "APay支付成功"
  17. }
  18. type BPayReq struct {
  19.   PayReq
  20.   Uid int64
  21. }
  22. func (p *BPayReq) Pay() string {
  23.   fmt.Println(p.OrderId)
  24.   fmt.Println(p.Uid)
  25.   return "BPay支付成功"
  26. }
复制代码
我们用APay和BPay两个结构体重写了Pay() 方法,假如需要添加一种新的付出方式, 只需要重写新的Pay() 方法即可。
工厂方法的优点就在于避免了创建者和具体产品之间的精密耦合,从而使得代码更轻易维护。
抽象工厂模式 Abstract Factory

抽象工厂模式基于工厂方法模式。两者的区别在于:工厂方法模式是创建出一种产品,而抽象工厂模式是创建出一类产品。这二种都属于工厂模式。
  1. //假设,有一个存储工厂,提供redis和mysql两种存储数据的方式。如果使用工厂方法模式,
  2. //我们就需要一个存储工厂,并提供SaveRedis方法和SaveMysql方法。
  3. //如果此时业务还需要分成存储散文和古诗两种载体,这两种载体都可以进行redis和mysql存储。
  4. //就可以使用抽象工厂模式,我们需要一个存储工厂作为父工厂,散文工厂和古诗工厂作为子工厂,
  5. //并提供SaveRedis方法和SaveMysql方法。
  6. package abstractfactory
  7. import "fmt"
  8. // SaveArticle 抽象模式工厂接口
  9. type SaveArticle interface {
  10.   CreateProse() Prose
  11.   CreateAncientPoetry() AncientPoetry
  12. }
  13. type SaveRedis struct{}
  14. func (*SaveRedis) CreateProse() Prose {
  15.   return &RedisProse{}
  16. }
  17. func (*SaveRedis) CreateAncientPoetry() AncientPoetry {
  18.   return &RedisProse{}
  19. }
  20. type SaveMysql struct{}
  21. func (*SaveMysql) CreateProse() Prose {
  22.   return &MysqlProse{}
  23. }
  24. func (*SaveMysql) CreateAncientPoetry() AncientPoetry {
  25.   return &MysqlProse{}
  26. }
  27. // Prose 散文
  28. type Prose interface {
  29.   SaveProse()
  30. }
  31. // AncientPoetry 古诗
  32. type AncientPoetry interface {
  33.   SaveAncientPoetry()
  34. }
  35. type RedisProse struct{}
  36. func (*RedisProse) SaveProse() {
  37.   fmt.Println("Redis Save Prose")
  38. }
  39. func (*RedisProse) SaveAncientPoetry() {
  40.   fmt.Println("Redis Save Ancient Poetry")
  41. }
  42. type MysqlProse struct{}
  43. func (*MysqlProse) SaveProse() {
  44.   fmt.Println("Mysql Save Prose")
  45. }
  46. func (*MysqlProse) SaveAncientPoetry() {
  47.   fmt.Println("Mysql Save Ancient Poetry")
  48. }
复制代码
  我们定义了存储工厂,也就是SaveArticle接口,并实现了CreateProse方法和CreateAncientPoetry方法,这2个方法分别用于创建散文工厂和古诗工厂。
  然后我们又分别为散文工厂和古诗工厂实现了SaveProse方法和SaveAncientPoetry方法,并用Redis结构体和Mysql结构体分别重写了2种存储方法。
  制作者模式 Builder

假设业务需要按步骤创建一系列复杂的对象,需要非常多的步骤,并且这些步骤之间是有联系的,有许多步骤的构造函数,制作者模式的用处就在于能够分步骤创建复杂对象。
  1. //定义每个步骤的代码,然后在一个构造函数中操作这些步骤,
  2. //我们需要一个主管类,用这个主管类来管理各步骤。只需要将所需参数传给一个构造函数,
  3. //构造函数再将参数传递给对应的主管类,最后由主管类完成后续所有建造任务。
  4. package builder
  5. import "fmt"
  6. // 建造者接口
  7. type Builder interface {
  8.   Part1()
  9.   Part2()
  10.   Part3()
  11. }
  12. // 管理类
  13. type Director struct {
  14.   builder Builder
  15. }
  16. // 构造函数
  17. func NewDirector(builder Builder) *Director {
  18.   return &Director{
  19.     builder: builder,
  20.   }
  21. }
  22. // 建造
  23. func (d *Director) Construct() {
  24.   d.builder.Part1()
  25.   d.builder.Part2()
  26.   d.builder.Part3()
  27. }
  28. type Builder struct {}
  29. func (b *Builder) Part1() {
  30.   fmt.Println("part1")
  31. }
  32. func (b *Builder) Part2() {
  33.   fmt.Println("part2")
  34. }
  35. func (b *Builder) Part3() {
  36.   fmt.Println("part3")
  37. }
复制代码
原型模式 Prototype

假如你盼望生成一个对象,其与另一个对象完全相同,该如何实现呢?
假如遍历对象的所有成员,将其依次复制到新对象中,会稍显麻烦,而且有些对象大概会有私有成员变量遗漏。
原型模式将这个克隆的过程委派给了被克隆的实际对象,被克隆的对象就叫做“原型”。
  1. //原型模式的用处就在于我们可以克隆对象,而无需与原型对象的依赖相耦合
  2. //例:依靠一个Clone方法实现了原型Type1的克隆
  3. package prototype
  4. import "testing"
  5. var manager *PrototypeManager
  6. type Type1 struct {
  7.   name string
  8. }
  9. func (t *Type1) Clone() *Type1 {
  10.   tc := *t
  11.   return &tc
  12. }
  13. func TestClone(t *testing.T) {
  14.   t1 := &Type1{
  15.     name: "type1",
  16.   }
  17.   t2 := t1.Clone()
  18.   if t1 == t2 {
  19.     t.Fatal("error! get clone not working")
  20.   }
  21. }
复制代码
单例模式 Singleton

存储着重要对象的全局变量,每每意味着“不安全”,因为你无法包管这个全局变量的值不会在项目的某个引用处被覆盖掉。
对数据的修改经常导致出乎料想的的结果和难以发现的bug。我在一处更新数据,却没有意识到软件中的另一处盼望着完全不同的数据,于是一个功能就失效了,而且找出故障的原因也会非常困难。
一个较好的解决方案是:将这样的“可变数据”封装起来,写一个查询方法专门用来获取这些值。
单例模式则更进一步:除了要为“可变数据”提供一个全局访问方法,它还要包管获取到的只有同一个实例。也就是说,假如你筹划用一个构造函数创建一个对象,单例模式将包管你得到的不是一个新的对象,而是之前创建过的对象,并且每次它所返回的都只有这同一个对象,也就是单例。这可以保护该对象实例不被篡改。
  1. //需要一个全局构造函数,返回一个私有的对象,无论何时调用,它总是返回相同的对象。
  2. package singleton
  3. import (
  4.   "sync"
  5. )
  6. // 单例实例
  7. type singleton struct {
  8.   Value int
  9. }
  10. type Singleton interface {
  11.   getValue() int
  12. }
  13. func (s singleton) getValue() int {
  14.   return s.Value
  15. }
  16. var (
  17.   instance *singleton
  18.   once     sync.Once
  19. )
  20. // 构造方法,用于获取单例模式对象
  21. func GetInstance(v int) Singleton {
  22.   once.Do(func() {
  23.     instance = &singleton{Value: v}
  24.   })
  25.   return instance
  26. }
复制代码
  单例实例singleton被生存为一个私有的变量,以包管不被其他包的函数引用。
  
  用构造方法GetInstance可以获得单例实例,函数中使用了sync包的once方法,以包管实例只会在首次调用时被初始化一次,之后再调用构造方法都只会返回同一个实例。
  结构型模式

结构型模式将一些对象和类组装成更大的结构体,并同时保持结构的机动和高效。
适配器模式 Adapter

假设一开始我们提,后供了A对象期随着业务迭代,又需要从A对象的基础之上衍生出不同的需求。假如有很多函数已经在线上调用了A对象,此时再对A对象进行修改就比力麻烦,因为需要考虑兼容题目
  1. //假设有2个接口,一个将厘米转为米,一个将米转为厘米。
  2. //提供一个适配器接口,使调用方不需要再操心调用哪个接口,直接由适配器做好兼容。
  3. package adapter
  4. // 提供一个获取米的接口和一个获取厘米的接口
  5. type Cm interface {
  6.   getLength(float64) float64
  7. }
  8. type M interface {
  9.   getLength(float64) float64
  10. }
  11. func NewM() M {
  12.   return &getLengthM{}
  13. }
  14. type getLengthM struct{}
  15. func (*getLengthM) getLength(cm float64) float64 {
  16.   return cm / 10
  17. }
  18. func NewCm() Cm {
  19.   return &getLengthCm{}
  20. }
  21. type getLengthCm struct{}
  22. func (a *getLengthCm) getLength(m float64) float64 {
  23.   return m * 10
  24. }
  25. // 适配器
  26. type LengthAdapter interface {
  27.   getLength(string, float64) float64
  28. }
  29. func NewLengthAdapter() LengthAdapter {
  30.   return &getLengthAdapter{}
  31. }
  32. type getLengthAdapter struct{}
  33. func (*getLengthAdapter) getLength(isType string, into float64) float64 {
  34.   if isType == "m" {
  35.     return NewM().getLength(into)
  36.   }
  37.   return NewCm().getLength(into)
  38. }
复制代码
桥接模式Bridge

假设一开始业务需要两种发送信息的渠道,sms和email,我们可以分别实现sms和email两个接口。之后随着业务迭代,又产生了新的需求,需要提供两种系统发送方式,systemA和systemB,并且这两种系统发送方式都应该支持sms和email渠道。
此时至少需要提供4种方法:systemA to sms,systemA to email,systemB to sms,systemB to email。
其实之前我们是在用继承的想法来看题目,桥接模式则盼望将继承关系变化为关联关系,使两个类独立存在。
详细:


  • 桥接模式需要将抽象和实现区分开;
  • 桥接模式需要将“渠道”和“系统发送方式”这两种种别区分开;
  • 最后在“系统发送方式”的类里调用“渠道”的抽象接口,使他们从继承关系变化为关联关系。
用一句话总结桥接模式的理念,就是:“将抽象与实现解耦,将不同种别的继承关系改为关联关系。
  1. package bridge
  2. import "fmt"
  3. // 两种发送消息的方法
  4. type SendMessage interface {
  5.   send(text, to string)
  6. }
  7. type sms struct{}
  8. func NewSms() SendMessage {
  9.   return &sms{}
  10. }
  11. func (*sms) send(text, to string) {
  12.   fmt.Println(fmt.Sprintf("send %s to %s sms", text, to))
  13. }
  14. type email struct{}
  15. func NewEmail() SendMessage {
  16.   return &email{}
  17. }
  18. func (*email) send(text, to string) {
  19.   fmt.Println(fmt.Sprintf("send %s to %s email", text, to))
  20. }
  21. // 两种发送系统
  22. type systemA struct {
  23.   method SendMessage
  24. }
  25. func NewSystemA(method SendMessage) *systemA {
  26.   return &systemA{
  27.     method: method,
  28.   }
  29. }
  30. func (m *systemA) SendMessage(text, to string) {
  31.   m.method.send(fmt.Sprintf("[System A] %s", text), to)
  32. }
  33. type systemB struct {
  34.   method SendMessage
  35. }
  36. func NewSystemB(method SendMessage) *systemB {
  37.   return &systemB{
  38.     method: method,
  39.   }
  40. }
  41. func (m *systemB) SendMessage(text, to string) {
  42.   m.method.send(fmt.Sprintf("[System B] %s", text), to)
  43. }
复制代码
对象树模式Object Tree

对象树模式的用处就在于可以利用多态和递归机制更方便地使用复杂树结构。
  1. //在Search方法中使用递归打印出了整棵树结构
  2. package objecttree
  3. import "fmt"
  4. type Component interface {
  5.   Parent() Component
  6.   SetParent(Component)
  7.   Name() string
  8.   SetName(string)
  9.   AddChild(Component)
  10.   Search(string)
  11. }
  12. const (
  13.   LeafNode = iota
  14.   CompositeNode
  15. )
  16. func NewComponent(kind int, name string) Component {
  17.   var c Component
  18.   switch kind {
  19.   case LeafNode:
  20.     c = NewLeaf()
  21.   case CompositeNode:
  22.     c = NewComposite()
  23.   }
  24.   c.SetName(name)
  25.   return c
  26. }
  27. type component struct {
  28.   parent Component
  29.   name   string
  30. }
  31. func (c *component) Parent() Component {
  32.   return c.parent
  33. }
  34. func (c *component) SetParent(parent Component) {
  35.   c.parent = parent
  36. }
  37. func (c *component) Name() string {
  38.   return c.name
  39. }
  40. func (c *component) SetName(name string) {
  41.   c.name = name
  42. }
  43. func (c *component) AddChild(Component) {}
  44. type Leaf struct {
  45.   component
  46. }
  47. func NewLeaf() *Leaf {
  48.   return &Leaf{}
  49. }
  50. func (c *Leaf) Search(pre string) {
  51.   fmt.Printf("leaf %s-%s\n", pre, c.Name())
  52. }
  53. type Composite struct {
  54.   component
  55.   childs []Component
  56. }
  57. func NewComposite() *Composite {
  58.   return &Composite{
  59.     childs: make([]Component, 0),
  60.   }
  61. }
  62. func (c *Composite) AddChild(child Component) {
  63.   child.SetParent(c)
  64.   c.childs = append(c.childs, child)
  65. }
  66. func (c *Composite) Search(pre string) {
  67.   fmt.Printf("%s+%s\n", pre, c.Name())
  68.   pre += " "
  69.   for _, comp := range c.childs {
  70.     comp.Search(pre)
  71.   }
  72. }
复制代码
装饰模式Decorator

偶尔候我们需要在一个类的基础上扩展另一个类,例如,一个披萨类,你可以在披萨类的基础上增加番茄披萨类和芝士披萨类。此时就可以使用装饰模式,简朴来说,装饰模式就是将对象封装到另一个对象中,用以为原对象绑定新的行为
  1. //定义pizza接口,创建了base类,实现了方法getPrice
  2. //用装饰模式的理念,实现了tomatoTopping和cheeseTopping类,他们都封装了pizza接口的getPrice方法
  3. package decorator
  4. type pizza interface {
  5.   getPrice() int
  6. }
  7. type base struct {}
  8. func (p *base) getPrice() int {
  9.   return 15
  10. }
  11. type tomatoTopping struct {
  12.   pizza pizza
  13. }
  14. func (c *tomatoTopping) getPrice() int {
  15.   pizzaPrice := c.pizza.getPrice()
  16.   return pizzaPrice + 10
  17. }
  18. type cheeseTopping struct {
  19.   pizza pizza
  20. }
  21. func (c *cheeseTopping) getPrice() int {
  22.   pizzaPrice := c.pizza.getPrice()
  23.   return pizzaPrice + 20
  24. }
复制代码
外观模式Facade

假如你需要初始化大量复杂的库或框架,就需要管理其依赖关系并且按精确的序次实行。此时就可以用一个外观类来统一处理这些依赖关系,以对其进行整合。
外观模式和制作者模式很相似。两者的区别在于,外观模式是一种结构型模式,她的目的是将对象组合起来,而不是像制作者模式那样创建出不同的产品。
  1. package facade
  2. import "fmt"
  3. // 初始化APIA和APIB
  4. type APIA interface {
  5.   TestA() string
  6. }
  7. func NewAPIA() APIA {
  8.   return &apiRunA{}
  9. }
  10. type apiRunA struct{}
  11. func (*apiRunA) TestA() string {
  12.   return "A api running"
  13. }
  14. type APIB interface {
  15.   TestB() string
  16. }
  17. func NewAPIB() APIB {
  18.   return &apiRunB{}
  19. }
  20. type apiRunB struct{}
  21. func (*apiRunB) TestB() string {
  22.   return "B api running"
  23. }
  24. // 外观类
  25. type API interface {
  26.   Test() string
  27. }
  28. func NewAPI() API {
  29.   return &apiRun{
  30.     a: NewAPIA(),
  31.     b: NewAPIB(),
  32.   }
  33. }
  34. type apiRun struct {
  35.   a APIA
  36.   b APIB
  37. }
  38. func (a *apiRun) Test() string {
  39.   aRet := a.a.TestA()
  40.   bRet := a.b.TestB()
  41.   return fmt.Sprintf("%s\n%s", aRet, bRet)
  42. }
复制代码
享元模式 Flyweight

在一些环境下,程序没有足够的内存容量支持存储大量对象,大概大量的对象存储着重复的状态,此时就会造成内存资源的浪费。
享元模式提出了这样的解决方案:假如多个对象中相同的状态可以共用,就能在在有限的内存容量中载入更多对象。享元模式盼望抽取出能在多个对象间共享的重复状态。
  1. //当程序需要存储大量对象且没有足够的内存容量时,可以考虑使用享元模式。
  2. //可以使用map结构来实现这一设想,假设需要存储一些代表颜色的对象
  3. package flyweight
  4. import "fmt"
  5. // 享元工厂
  6. type ColorFlyweightFactory struct {
  7.   maps map[string]*ColorFlyweight
  8. }
  9. var colorFactory *ColorFlyweightFactory
  10. func GetColorFlyweightFactory() *ColorFlyweightFactory {
  11.   if colorFactory == nil {
  12.     colorFactory = &ColorFlyweightFactory{
  13.       maps: make(map[string]*ColorFlyweight),
  14.     }
  15.   }
  16.   return colorFactory
  17. }
  18. func (f *ColorFlyweightFactory) Get(filename string) *ColorFlyweight {
  19.   color := f.maps[filename]
  20.   if color == nil {
  21.     color = NewColorFlyweight(filename)
  22.     f.maps[filename] = color
  23.   }
  24.   return color
  25. }
  26. type ColorFlyweight struct {
  27.   data string
  28. }
  29. // 存储color对象
  30. func NewColorFlyweight(filename string) *ColorFlyweight {
  31.   // Load color file
  32.   data := fmt.Sprintf("color data %s", filename)
  33.   return &ColorFlyweight{
  34.     data: data,
  35.   }
  36. }
  37. type ColorViewer struct {
  38.   *ColorFlyweight
  39. }
  40. func NewColorViewer(name string) *ColorViewer {
  41.   color := GetColorFlyweightFactory().Get(name)
  42.   return &ColorViewer{
  43.     ColorFlyweight: color,
  44.   }
  45. }
复制代码
  定义一个享元工厂,使用map存储相同对象(key)的状态(value)。这个享元工厂可以使我们更方便和安全的访问各种享元,包管其状态不被修改。
  
  定义NewColorViewer方法,它会调用享元工厂的Get方法存储对象,而在享元工厂的实现中可以看到,相同状态的对象只会占用一次。
  代理模式Proxy

假如你需要在访问一个对象时,有一个像“代理”一样的脚色,她可以在访问对象之前为你进行缓存查抄、权限判断等访问控制,在访问对象之后为你进行结果缓存、日记记录等结果处理,可以考虑使用代理模式。
回忆一下一些web框架的router模块,当客户端访问一个接口时,在终极实行对应的接口之前,router模块会实行一些事前操纵,进行权限判断等操纵,在实行之后还会记录日记,这就是典型的代理模式。
  1. //代理模式需要一个代理类,包含执行真实对象所需的成员变量,由代理类管理整个生命周期。
  2. //定义代理类Proxy,执行Proxy之后,在调用真实对象Real之前,先调用事前对象Pre,
  3. //在执行真实对象Real之后,调用事后对象After。
  4. package proxy
  5. import "fmt"
  6. type Subject interface {
  7.   Proxy() string
  8. }
  9. // 代理
  10. type Proxy struct {
  11.   real RealSubject
  12. }
  13. func (p Proxy) Proxy() string {
  14.   var res string
  15.   // 在调用真实对象之前,检查缓存,判断权限,等等
  16.   p.real.Pre()
  17.   // 调用真实对象
  18.   p.real.Real()
  19.   // 调用之后的操作,如缓存结果,对结果进行处理,等等
  20.   p.real.After()
  21.   return res
  22. }
  23. // 真实对象
  24. type RealSubject struct{}
  25. func (RealSubject) Real() {
  26.   fmt.Print("real")
  27. }
  28. func (RealSubject) Pre() {
  29.   fmt.Print("pre:")
  30. }
  31. func (RealSubject) After() {
  32.   fmt.Print(":after")
  33. }
复制代码
行为型模式

行为型模式处理对象和类之间的通讯,并使其保持高效的沟通和委派。
责任链模式Chain of Responsibility

假设我们要让程序按序次实行多个处理者,并且处理者的序次可以改变的需求,可以考虑使用责任链模式。责任链模式使用了类似链表的结构
  1. package chain
  2. import "fmt"
  3. type department interface {
  4.   execute(*Do)
  5.   setNext(department)
  6. }
  7. type aPart struct {
  8.   next department
  9. }
  10. func (r *aPart) execute(p *Do) {
  11.   if p.aPartDone {
  12.     fmt.Println("aPart done")
  13.     r.next.execute(p)
  14.     return
  15.   }
  16.   fmt.Println("aPart")
  17.   p.aPartDone = true
  18.   r.next.execute(p)
  19. }
  20. func (r *aPart) setNext(next department) {
  21.   r.next = next
  22. }
  23. type bPart struct {
  24.   next department
  25. }
  26. func (d *bPart) execute(p *Do) {
  27.   if p.bPartDone {
  28.     fmt.Println("bPart done")
  29.     d.next.execute(p)
  30.     return
  31.   }
  32.   fmt.Println("bPart")
  33.   p.bPartDone = true
  34.   d.next.execute(p)
  35. }
  36. func (d *bPart) setNext(next department) {
  37.   d.next = next
  38. }
  39. type endPart struct {
  40.   next department
  41. }
  42. func (c *endPart) execute(p *Do) {
  43.   if p.endPartDone {
  44.     fmt.Println("endPart Done")
  45.   }
  46.   fmt.Println("endPart")
  47. }
  48. func (c *endPart) setNext(next department) {
  49.   c.next = next
  50. }
  51. type Do struct {
  52.   aPartDone   bool
  53.   bPartDone   bool
  54.   endPartDone bool
  55. }
复制代码
  1. 实现了方法execute和setNext,并定义了aPart、bPart、endPart这3个处理者,每个处理者都可以通过execute方法执行其对应的业务代码,并可以通过setNext方法决定下一个处理者是谁。除了endPart是最终的处理者之外,在它之前的处理者aPart、bPart的顺序都可以任意调整。
复制代码
下令模式Command

假如你之后对基类进行修改,很大概会影响到其他功能,这使项目变得不稳定了。
让下令和对应功能解耦,并能根据不同的哀求将其方法参数化。
  1. package command
  2. import "fmt"
  3. // 请求者
  4. type button struct {
  5.   command command
  6. }
  7. func (b *button) press() {
  8.   b.command.execute()
  9. }
  10. // 具体命令接口
  11. type command interface {
  12.   execute()
  13. }
  14. type onCommand struct {
  15.   device device
  16. }
  17. func (c *onCommand) execute() {
  18.   c.device.on()
  19. }
  20. type offCommand struct {
  21.   device device
  22. }
  23. func (c *offCommand) execute() {
  24.   c.device.off()
  25. }
  26. // 接收者
  27. type device interface {
  28.   on()
  29.   off()
  30. }
  31. type tv struct{}
  32. func (t *tv) on() {
  33.   fmt.Println("Turning tv on")
  34. }
  35. func (t *tv) off() {
  36.   fmt.Println("Turning tv off")
  37. }
  38. type airConditioner struct{}
  39. func (t *airConditioner) on() {
  40.   fmt.Println("Turning air conditioner on")
  41. }
  42. func (t *airConditioner) off() {
  43.   fmt.Println("Turning air conditioner off")
  44. }
复制代码
  实现了哀求者button,下令接口command,接收者device。哀求者button就像是实行开启或关闭的遥控器,下令接口command则是一个中间层,它使我们的哀求者和接收者解藕。
  迭代器模式Iterator

迭代器模式用于遍历聚会合的元素,无论聚集的数据结构是怎样的。
  1. package iterator
  2. // 集合接口
  3. type collection interface {
  4.   createIterator() iterator
  5. }
  6. // 具体的集合
  7. type part struct {
  8.   title  string
  9.   number int
  10. }
  11. type partCollection struct {
  12.   part
  13.   parts []*part
  14. }
  15. func (u *partCollection) createIterator() iterator {
  16.   return &partIterator{
  17.     parts: u.parts,
  18.   }
  19. }
  20. // 迭代器
  21. type iterator interface {
  22.   hasNext() bool
  23.   getNext() *part
  24. }
  25. // 具体的迭代器
  26. type partIterator struct {
  27.   index int
  28.   parts []*part
  29. }
  30. func (u *partIterator) hasNext() bool {
  31.   if u.index < len(u.parts) {
  32.     return true
  33.   }
  34.   return false
  35. }
  36. func (u *partIterator) getNext() *part {
  37.   if u.hasNext() {
  38.     part := u.parts[u.index]
  39.     u.index++
  40.     return part
  41.   }
  42.   return nil
  43. }
复制代码
中介者模式Mediator

中介者模式试图解决网状关系的复杂关联,低沉对象间的耦合度。
举个例子,假设一个十字路口上的车都是对象,它们会实行不同的操纵,前往不同的目的地,那么在十字路口指挥的交警就是“中介者”。
各个对象通过实行中介者接口,再由中介者维护对象之间的联系。这能使对象变得更独立,比力适合用在一些对象是网状关系的案例上。
  1. //设有p1,p2,p33个发送者,p1发送的消息p2能收到,p2发送的消息p1能收到,p3发送的消息p1p2能收到
  2. //定义p1,p2,p3这3个对象,然后实现中介者sendMessage
  3. package mediator
  4. import (
  5.   "fmt"
  6. )
  7. type p1 struct{}
  8. func (p *p1) getMessage(data string) {
  9.   fmt.Println("p1 get message: " + data)
  10. }
  11. type p2 struct{}
  12. func (p *p2) getMessage(data string) {
  13.   fmt.Println("p2 get message: " + data)
  14. }
  15. type p3 struct{}
  16. func (p *p3) getMessage(data string) {
  17.   fmt.Println("p3 get message: " + data)
  18. }
  19. type Message struct {
  20.   p1 *p1
  21.   p2 *p2
  22.   p3 *p3
  23. }
  24. func (m *Message) sendMessage(i interface{}, data string) {
  25.   switch i.(type) {
  26.   case *p1:
  27.     m.p2.getMessage(data)
  28.   case *p2:
  29.     m.p1.getMessage(data)
  30.   case *p3:
  31.     m.p1.getMessage(data)
  32.     m.p2.getMessage(data)
  33.   }
  34. }
复制代码
备忘录模式Memento

如何在程序中实现生存和恢复的功能?
需要提供生存和恢复的功能,当生存功能被调用时,就会生成当前对象的快照,在恢复功能被调用时,就会用之前生存的快照覆盖当前的快照。这可以使用备忘录模式来做。
  1. //定义textMemento结构体用于保存当前快照,并在Load方法中将快照覆盖到当前内容
  2. package memento
  3. import "fmt"
  4. type Memento interface{}
  5. type Text struct {
  6.   content string
  7. }
  8. type textMemento struct {
  9.   content string
  10. }
  11. func (t *Text) Write(content string) {
  12.   t.content = content
  13. }
  14. func (t *Text) Save() Memento {
  15.   return &textMemento{
  16.     content: t.content,
  17.   }
  18. }
  19. func (t *Text) Load(m Memento) {
  20.   tm := m.(*textMemento)
  21.   t.content = tm.content
  22. }
  23. func (t *Text) Show() {
  24.   fmt.Println("content:", t.content)
  25. }
复制代码
观察者模式Observer

在一个对象的状态被改变时,其他对象能作为其“观察者”而被通知
将自身的状态改变就会通知给其他对象的对象称为“发布者”,关注发布者状态变化的对象则称为“订阅者”。
  1. //实现一个通知notify方法,在发布者的状态改变时执行
  2. package observer
  3. import "fmt"
  4. // 发布者
  5. type Subject struct {
  6.   observers []Observer
  7.   content   string
  8. }
  9. func NewSubject() *Subject {
  10.   return &Subject{
  11.     observers: make([]Observer, 0),
  12.   }
  13. }
  14. // 添加订阅者
  15. func (s *Subject) AddObserver(o Observer) {
  16.   s.observers = append(s.observers, o)
  17. }
  18. // 改变发布者的状态
  19. func (s *Subject) UpdateContext(content string) {
  20.   s.content = content
  21.   s.notify()
  22. }
  23. // 通知订阅者接口
  24. type Observer interface {
  25.   Do(*Subject)
  26. }
  27. func (s *Subject) notify() {
  28.   for _, o := range s.observers {
  29.     o.Do(s)
  30.   }
  31. }
  32. // 订阅者
  33. type Reader struct {
  34.   name string
  35. }
  36. func NewReader(name string) *Reader {
  37.   return &Reader{
  38.     name: name,
  39.   }
  40. }
  41. func (r *Reader) Do(s *Subject) {
  42.   fmt.Println(r.name + " get " + s.content)
  43. }
复制代码
状态模式 State

假如一个对象的实现方法会根据自身的状态而改变,就可以使用状态模式。
例:假设有一个开门的方法,门的状态在一开始是“关闭”,你可以实行open方法和close方法,当你实行了open方法,门的状态就酿成了“开启”,再实行open方法就不会实行开门的功能,而是返回“门已开启”,假如实行close方法,门的状态就酿成了“关闭”,再实行close方法就不会实行关门的功能,而是返回“门已关闭”。
需要为一个门对象提供3种状态下的open和close方法:


  • “开启”状态下,open方法返回“门已开启”,close方法返回“关闭乐成”。
  • “关闭”状态下,open方法返回“开启乐成”,close方法返回“门已关闭”。
  • “损坏”状态下,open方法返回“门已损坏,无法开启”,close方法返回“门已损坏,无法关闭”。
  1. //门对象door实现了open和close方法,在方法中只需要调用当前状态currentState的open和close方法
  2. package state
  3. import "fmt"
  4. // 不同状态需要实现的接口
  5. type state interface {
  6.   open(*door)
  7.   close(*door)
  8. }
  9. // 门对象
  10. type door struct {
  11.   opened  state
  12.   closed  state
  13.   damaged state
  14.   currentState state // 当前状态
  15. }
  16. func (d *door) open() {
  17.   d.currentState.open(d)
  18. }
  19. func (d *door) close() {
  20.   d.currentState.close(d)
  21. }
  22. func (d *door) setState(s state) {
  23.   d.currentState = s
  24. }
  25. // 开启状态
  26. type opened struct{}
  27. func (o *opened) open(d *door) {
  28.   fmt.Println("门已开启")
  29. }
  30. func (o *opened) close(d *door) {
  31.   fmt.Println("关闭成功")
  32. }
  33. // 关闭状态
  34. type closed struct{}
  35. func (c *closed) open(d *door) {
  36.   fmt.Println("开启成功")
  37. }
  38. func (c *closed) close(d *door) {
  39.   fmt.Println("门已关闭")
  40. }
  41. // 损坏状态
  42. type damaged struct{}
  43. func (a *damaged) open(d *door) {
  44.   fmt.Println("门已损坏,无法开启")
  45. }
  46. func (a *damaged) close(d *door) {
  47.   fmt.Println("门已损坏,无法关闭")
  48. }
复制代码
策略模式Strategy

假设需要实现一组出行的功能,出现的方案可以选择步行、骑行、开车,最简朴的做法就是分别实现这3种方法供客户端调用。但这样做就使对象与其代码实现变得耦合了,客户端需要决定出行方式,然后决定调用步行出行、骑行出行、开车出行等方法,这不符合开闭原则
而策略模式的区别在于,它会将这些出行方案抽取到一组被称为策略的类中,客户端还是调用同一个出行对象,不需要关注实现细节,只需要在参数中指定所需的策略即可。
  1. //定义strategy一组策略接口,为其实现了Walk、Ride、Drive算法
  2. //客户端只需要执行traffic方法即可,无需关注实现细节
  3. package strategy
  4. import "fmt"
  5. type Travel struct {
  6.   name     string
  7.   strategy Strategy
  8. }
  9. func NewTravel(name string, strategy Strategy) *Travel {
  10.   return &Travel{
  11.     name:     name,
  12.     strategy: strategy,
  13.   }
  14. }
  15. func (p *Travel) traffic() {
  16.   p.strategy.traffic(p)
  17. }
  18. type Strategy interface {
  19.   traffic(*Travel)
  20. }
  21. type Walk struct{}
  22. func (w *Walk) traffic(t *Travel) {
  23.   fmt.Println(t.name + " walk")
  24. }
  25. type Ride struct{}
  26. func (w *Ride) traffic(t *Travel) {
  27.   fmt.Println(t.name + " ride")
  28. }
  29. type Drive struct{}
  30. func (w *Drive) traffic(t *Travel) {
  31.   fmt.Println(t.name + " drive")
  32. }
复制代码
模板方法模式Template Method

将算法分解为一系列步骤,然后在一个模版方法中依次调用这些步骤,客户端不需要相识各个步骤的实现细节,只需要调用模版。
  1. package templatemethod
  2. import "fmt"
  3. type PrintTemplate interface {
  4.   Print(name string)
  5. }
  6. type template struct {
  7.   isTemplate PrintTemplate
  8.   name       string
  9. }
  10. func (t *template) Print() {
  11.   t.isTemplate.Print(t.name)
  12. }
  13. type A struct{}
  14. func (a *A) Print(name string) {
  15.   fmt.Println("a: " + name)
  16.   // 业务代码……
  17. }
  18. type B struct{}
  19. func (b *B) Print(name string) {
  20.   fmt.Println("b: " + name)
  21.   // 业务代码……
  22. }
复制代码
访问者模式Visitor

在不改变类的对象结构的前提下增加新的操纵。
  1. package visitor
  2. import "fmt"
  3. type Shape interface {
  4.   accept(visitor)
  5. }
  6. type square struct{}
  7. func (s *square) accept(v visitor) {
  8.   v.visitForSquare(s)
  9. }
  10. type circle struct{}
  11. func (c *circle) accept(v visitor) {
  12.   v.visitForCircle(c)
  13. }
  14. type visitor interface {
  15.   visitForSquare(*square)
  16.   visitForCircle(*circle)
  17. }
  18. type sideCalculator struct{}
  19. func (a *sideCalculator) visitForSquare(s *square) {
  20.   fmt.Println("square side")
  21. }
  22. func (a *sideCalculator) visitForCircle(s *circle) {
  23.   fmt.Println("circle side")
  24. }
  25. type radiusCalculator struct{}
  26. func (a *radiusCalculator) visitForSquare(s *square) {
  27.   fmt.Println("square radius")
  28. }
  29. func (a *radiusCalculator) visitForCircle(c *circle) {
  30.   fmt.Println("circle radius")
  31. }
复制代码
6、筹划模式原则

(1)开闭原则


  • 软件应该对扩展开放,对修改关闭。
  • 对系统进行扩展,而无需修改现有的代码。这可以低沉软件的维护成本,同时也增加可扩展性。
(2)里氏更换原则


  • 任何基类可以出现的地方,子类肯定可以出现。
  • 里氏更换原则是对开闭原则的补充,实现开闭原则的关键步骤就是抽象化,基类与子类的关系就是要尽大概的抽象化。
(3)依赖倒置原则


  • 面向接口编程,抽象不应该依赖于具体类,具体类应当依赖于抽象。
  • 这是为了减少类间的耦合,使系统更相宜于扩展,也更便于维护。
(4)单一职责原则


  • 一个类应该只有一个发生变化的原因。
  • 一个类承载的越多,耦合度就越高。假如类的职责单一,就可以低沉堕落的风险,也可以提高代码的可读性。
(5)最少知道原则


  • 一个实体应当尽量少地与其他实体之间发生相互作用。
  • 还是为了低沉耦合,一个类与其他类的关联越少,越易于扩展。
(6)接口分离原则


  • 使用多个专门的接口,而不使用高耦合的单一接口。
  • 避免同一个接口占用过多的职责,更明确的划分,可以低沉耦合。高耦合会导致程序不易扩展,提高堕落的风险。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

八卦阵

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表