完整代码链接:https://github.com/ziyifast/easy_design_mode
🚀:欢迎star哦~
1.1 简单工厂模式(simple factory):不同协议有生成不同downloader
①解析
go 语言没有构造函数,所以我们一般是通过 NewXXX 函数来初始化相关类。 NewXXX 函数返回接口时就是简单工厂模式,也就是说 Golang 的一般推荐做法就是简单工厂。
- 在这个 simplefactory 包中只有API 接口和 NewAPI 函数为包外可见,封装了实现细节。
②代码
1.2 工厂方法模式(factory method):不同品牌手机有不同工厂
①解析
工厂方法模式使用子类的方式延迟生成对象到子类中实现。
Go 中不存在继承 所以使用匿名组合来实现
示例步骤:
- 定义接口type operator interface
- 参数a
- 参数b
- result:具体业务方法
- 定义type BaseFactory struct:提供方法,用于设置a、b参数
- 参数a
- 参数b
- 根据不同操作,定义不同工厂类(addFactory、minusFactory)
- addFactory实现operator的result:a+b
- minusFactory实现operator的result:a-b
- addFactory、minusFactory分别提供Create方法
简单工厂:唯一工厂类,一个产品抽象类,工厂类的创建方法依据入参判断并创建具体产品对象。
工厂方法:多个工厂类,一个产品抽象类,利用多态创建不同的产品对象,避免了大量的if-else判断。
抽象工厂:多个工厂类,多个产品抽象类,产品子类分组,同一个工厂实现类创建同组中的不同产品,减少了工厂子类的数量。
②代码
1.3 创建者模式(builder):上架商品包含命名+设置价格+数量等
①解析
将build一个物品拆分为几个部分
示例步骤:
- 定义接口type goodsBuilder interface
- setName
- setPrice
- setCount
- *Goods
- 定义具体实现结构体type ConcreteBuilder struct
- 实现goodsBuilder接口
- 提供NewGoodsBuilder接口,返回ConcreteBuilder实现类
②代码
1.4 原型模式(prototype):clone对象,复制自身
①解析
原型模式使对象能复制自身,并且暴露到接口中,使客户端面向接口编程时,不知道接口实际对象的情况下生成新的对象。
原型模式配合原型管理器使用,使得客户端在不知道具体类的情况下,通过接口管理器得到新的实例,并且包含部分预设定配置。
示例步骤:
- 定义接口type Prototype interface
- 包含clone方法:Clone() Prototype
- 定义结构体type ConcretePrototype struct
- 实现clone方法:Clone() Prototype
②代码
1.5 单例模式(singleton):一个类只有一个实例
①解析
使用懒惰模式的单例模式,使用once.Do加锁保证线程安全
保证只有单个实例
单例模式:
- 懒汉式:用到时才实例化(GetInstance),通过once.Do保证只加载一次
- 饿汉式:一开始就实例化(init)
②代码
2.1 外观模式(facade):audioFixer+videoFixer
①解析
外观模式也叫门面模式,是一种结构型设计模式,它提供了一个统一的接口来访问子系统中的一组接口。这种模式通过定义一个高层接口来隐藏子系统的复杂性,使子系统更容易使用。
在Go语言中,我们可以使用结构体和接口来实现外观模式。
示例:
- 定义audioFixer,实现fixer
- 定义videoFixer,实现fixer
- 定义AudioAndVideoFixer,组合audioFixer和videoFixer
②代码
2.2 适配器模式(adapter):阿里支付、维信支付等
①解析
适配器适合用于解决新旧系统(或新旧接口)之间的兼容问题,而不建议在一开始就直接使用
- 适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间
关键代码:
适配器中持有旧接口对象,并实现新接口
示例:
- 定义阿里支付接口 type AliPayInterface interface,包含Pay方法
- 定义微信支付接口 type WeChatPayInterface interface,包含Pay方法
- 分别定义阿里支付和微信支付的实现类 AliPay 和 WeChatPay struct
- 定义目标接口 type TargetPayInterface interface,包含DealPay方法
- 定义TargetPayInterface接口实现类 PayAdapter struct,实现DealPay方法
- 内部持有AliPayInterface和WeChatPayInterface对象,根据类型分别调用其Pay方法
②代码
2.3 代理模式(proxy):支付前的签名校验等
①解析
代理模式用于延迟处理操作或者在进行实际操作前后进行其它处理。
示例:
- 定义PaymentService interface接口,包含pay方法
- 定义Alipay struct,实现PaymentService interface接口
- 定义paymentProxy struct,包含realPay(实现了PaymentService interface的结构体)
- paymentProxy实现pay方法,调用realPay的pay方法之前和之后分别来添加一些额外的操作
②代码
2.4 享元模式(flyweight):优先从内存map中获取对象
①解析
享元模式从对象中剥离出不发生改变且多个实例需要的重复数据,独立出一个享元,使多个对象共享,从而节省内存以及减少对象数量。
示例:
- 定义结构体ImageFlyweight struct,用于展示图像信息
- 定义ImageFlyweightFactory struct工厂,包含:maps map[string]*ImageFlyweight
- 提供方法:GetImageFlyweight(name string) *ImageFlyweight
- 先从map中获取,如果map中存在,则直接返回;如果不存在则New一个ImageFlyweight,存入map中,然后返回
- 提供方法GetImageFlyweightFactory,用于获取工厂
②代码
2.5 装饰模式(decorator):换个皮肤,增加car的价钱
①解析
装饰模式使用对象组合的方式动态改变或增加对象行为。
Go语言借助于匿名组合和非入侵式接口可以很方便实现装饰模式。
使用匿名组合,在装饰器中不必显式定义转调原对象方法。
示例:
- 定义PriceDecorator interface接口
- 包含DecoratePrice(c Car) Car方法,用于增加车的price
- 定义ExtraPriceDecorator结构体,实现PriceDecorator接口
- 包含ExtraPrice字段,用于给传入的car增加额外的price,最后返回car
②代码
2.6 桥模式(bridge):不同电脑可桥接不同打印机
①解析
桥接模式分离抽象部分和实现部分。使得两部分独立扩展。
桥接模式类似于策略模式,区别在于策略模式封装一系列算法使得算法可以互相替换。
策略模式使抽象部分和实现部分分离,可以独立变化。
如果你想要拆分或重组一个具有多重功能的庞杂类(例如能与多个数据库服务器进行交互的类),可以使用桥接模式。
如果你希望在几个独立维度上扩展一个类,可使用该模式。
如果你需要在运行时切换不同实现方法,可使用桥接模式。
示例:
- 定义Printer interface接口,包含print方法。
- 定义Computer interface接口,包括SetPrinter(Printer)以及print方法。
- 创建不同Printer、Computer通过SetPrinter方法,来实现电脑和打印机的排列组合
②代码
2.7 组合模式(composite):目录包含多个文件
①解析
组合模式统一对象和对象集,使得使用相同接口使用对象和对象集。
组合模式常用于树状结构,用于统一叶子节点和树节点的访问,并且可以用于应用某一操作到所有子节点。
示例:以飞书文档接口为例,一个目录下面可以包含多个子文件
我们最先想到的做法就是:将文件和目录放在一个类中,新增一个字段,用于判断是文件还是目录。但这样并不优雅。因为文件和目录是不同的,各自有各自的特性,将特有的内容放到一个类里,不满足单一职责原则。
下面将展示通过组合模式来实现文档管理结构
- 定义FileSystem interface,抽取文档和目录的公共部分
- Display(separator string)
- 定义FileCommon struct,抽取文件和目录的公共部分
- fileName string
- SetFileName(fileName string)
- 定义FileNode struct,用于表示文件类。并实现Display方法
- FileCommon
- 定义DirectoryNode struct,用于表示目录类。并实现Display方法。因为目录下可以存放多个文件,因此需要提供addFile方法
- FileCommon
- nodes []FileSystemNode
- Add(f FileSystemNode)
②代码
3.1 中介者模式(mediator):租房中介
①解析
中介者模式封装对象之间互交,使依赖变的简单,并且使复杂互交简单化,封装在中介者中。
例子中的中介者使用单例模式生成中介者。
中介者的change使用switch判断类型。
使用场景:
- 当一些对象和其他对象紧密耦合以致难以对其进行修改时,可使用中介者模式。
- 当组件因过于依赖其他组件而无法在不同应用中复用时,可使用中介者模式。
- 如果为了能在不同情景下复用一些基本行为,导致你需要被迫创建大量组件子类时,可使用中介者模式。
示例:
- 定义MessageMediator interface中介者接口,包含sendMessage(msg string, user User)、receiveMessage() string方法
- 定义具体中介者实现类,ChatRoom struct,属性包含Message string
- 实现sendMessage方法,将消息发送给用户
- 定义type User struct
- name string
- mediator Mediator
- 用户通过mediator发送消息,通过mediator(chatroom)接受消息
②代码
3.2 观察者模式(observer):发布订阅
①解析
观察者模式用于触发联动。
一个对象的改变会触发其它观察者的相关动作,而此对象无需关心连动对象的具体实现。
关键:被观察者持有了集合存放观察者 (收通知的为观察者);类比消息队列的发布订阅,你订阅了此类消息,当有消息来时,我就通知你
示例:
- 定义Customer interface,包含update() 方法
- 定义CustomerA、CustomerB结构体,实现update()方法
- 定义newsOffice struct,包含customers字段,用于存储观察者,后续有事件发生时通知观察者
- customers []Customer
- func addCustomer(customer Customer)
- func removeCustomer(customer Customer)
- func newsComing() 新报纸到来,调用notifyAll方法通知所有观察者
- func notifyAll()
②代码
3.3 命令模式(command):命令服务员倒水、命令厨师炒菜
①解析
命令模式本质是把某个对象的方法调用封装到对象中,方便传递、存储、调用。
示例中把主板单中的启动(start)方法和重启(reboot)方法封装为命令对象,再传递到主机(box)对象中。于两个按钮进行绑定:
第一个机箱(box1)设置按钮1(button1) 为开机按钮2(button2)为重启。
第二个机箱(box1)设置按钮2(button2) 为开机按钮1(button1)为重启。
从而得到配置灵活性。
除了配置灵活外,使用命令模式还可以用作:
批处理
任务队列
undo, redo
等把具体命令封装到对象中使用的场合
示例:
- 定义ICommand interface接口,包含execute()方法
- 定义Invoker struct,包含commands []ICommand
- 包含Call():用于执行commands中的所有方法
- 定义TV struct,包含shutdown和turnOn方法
- 定义ShutdownCommand struct,包含tv,实现execute()方法
- t.tv.Shutdown()
- 定义TurnOnCommand struct,包含tv,实现execute()方法
- t.tv.turnOn()
②代码
3.4 迭代器模式(iterator):集合遍历
①解析
迭代器模式用于使用相同方式送代不同类型集合或者隐藏集合类型的具体实现。
可以使用迭代器模式使遍历同时应用送代策略,如请求新对象、过滤、处理对象等。
示例:
- 定义Iterator interface
- Next() interface{}
- HasNext() bool
- 定义NumberIterator struct结构体,实现Iterator接口
- 定义Numbers struct
- numbers []int
- func Iterator(),用于获取number的迭代器
②代码
3.5 模板方法模式(template method):炒菜之前都需要打开煤气+开火+加油等
①解析
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
- 通用步骤在抽象类中实现,变化的步骤在具体的子类中实现
- 做饭,打开煤气,开火,(做饭), 关火,关闭煤气。除了做饭其他步骤都是相同的,抽到抽象类中实现
示例:
- 定义type Cooker interface{},包含做饭的全部步骤,open()、openfire()、cook()、close()、closefire()等
- 定义type CookMenu struct抽象类,实现做饭的通用步骤,如:打开煤气、打开开关、关闭煤气、关闭开关。具体的做饭内容cook()在子类中实现
- 定义type ChaojiDan struct,继承CookMenu(属性里包含CookMenu),实现具体做饭的步骤:炒鸡蛋
- CookMenu
- func (ChaoJiDan) cook() {
fmt.Println(“做炒鸡蛋”)
}
②代码
3.6 策略模式(strategy):支付可采用现金支付、信用卡支付(策略)
①解析
定义一系列算法,让这些算法在运行时可以互换,使得分离算法,符合开闭原则。
示例:
- 定义PaymentStrategy interface 接口
- Pay(amount float64) error
- 定义实现类,CreditCardPaymentStrategy、CashPaymentStrategy
- 分别实现Pay(amount float64) error方法
- 定义上下文对象类PaymentContext
- amount float64
- strategy PaymentStrategy
- PaymentContext提供Pay()方法,调用strategy.Pay(amount)方法
- p.strategy.Pay(p.amount)
- 提供NewPaymentContext方法,返回*PaymentContext对象
- 根据不同策略,调用NewPaymentContext方法,返回不同PaymentContext对象,实现不同的支付
②代码
3.7 状态模式(state):QQ包括离线、在线、忙碌等状态
①解析
状态模式用于分离状态和行为。
示例:
- 定义ActionState interface接口,包含:状态所关联的 查看、评论、发布行为
- View()
- Comment()
- Post()
- 定义ActionState的实现类
- NormalState:账号正常状态
- RestrictState:账号受限制状态
- CloseState:账号被封状态
- 定义type Account struct
- state *ActionState
- healthValue:健康值对应不同的状态
- 分别实现View、Comment、Post方法,调用state的方法:a.state.View()
- 实现changeState方法,根据healthValue设置state
- 定义NewAccount(healthValue int) *Account:创建一个账号并设置健康值
②
3.8 备忘录模式(memento):文本的撤销与重做
①解析
备忘录模式用于保存程序内部状态到外部,又不希望暴露内部状态的情形。
允许在不破坏封装性的前提下保存和恢复对象的内部状态,程序内部状态使用窄接口传递给外部进行存储,从而不暴露程序实现细节。
备忘录模式同时可以离线保存内部状态,如保存到数据库,文件等。
该模式涉及三个主要角色:
- Originator(发起人):Originator 是拥有内部状态的对象
- Memento(备忘录):Memento 是 Originator 的快照
- Caretaker(负责人):Caretaker 负责备份和恢复 Memento。
示例:
state暂时用string来代替,实际可用struct
案例演示:实现一个文本编辑器,提供撤销和重做功能
- 定义Originator interface, Originator 发起人:用于保存或者恢复当前状态(备忘录)
- Save() Memento
- Restore(m Memento)
- 定义Memento interface ,提供方法用于获取当前状态【备忘录:记录当前状态】
- GetState() string
- 定义TextMemento struct,实现Memento接口,保存state
- 定义textEditor struct,实现Originator接口
- state string
- func Save() Memento
- func Restore(m Memento)
- func SetState(state string)
- 定义Caretaker struct
- mementos []Memento
- currentIndex int
- func AddMemento(m Memento)
- func Undo(t *TextEditor)
- func Redo(t *TextEditor)
②代码
3.9 解释器模式(interpreter):SQL语法解析器
①解析
解释器模式(Interpreter Pattern)是一种行为设计模式,它定义了一种语言,用于解释一些特定的领域问题。
在该模式中,将语言中的元素映射到类中,并定义它们之间的关系。然后,可以使用这些类来解释表达式,以解决特定的问题。
应用场景:
- 处理配置文件:json、yaml
- 模板引擎:模板引擎处理模板和一组变量以产生输出。模板是DSL的一个例子,可以使用Interpreter来解析和处理模板。
- 数学表达式计算器
解释器模式中的关键组件:
表达式接口:表示抽象语法树的元素并定义解释表达式的方法。
具体表达式:实现表达式接口的结构,表示语言语法的各种规则或元素。
上下文对象:用于保存解释过程中所需的任何必要信息或状态。
Parser 或 Builder:负责根据输入表达式构建抽象语法树的组件。
示例:
构建一个计算器
- 定义Expression interface接口,包含Interpret() int方法
- 定义NumberExpression struct,实现Interpret() int方法
- val int
- 定义AdditionExpression struct,实现Interpret() int方法,返回两数之和
- left Expression
- right Expression
- func Interpret() int {
}
- 定义SubtractionExpression struct,实现Interpret() int方法,返回两数之差
- left Expression
- right Expression
- func Interpret() int {
}
- 定义Parser struct,存储输入表达式exp、index解析索引位置、prev 上一个表达式
- exp []string
- index int
- prev Expression
- func Parse() Expression {
} - func newAdditionExpression() Expression{
} - func newSubtractionExpression() Expression{
}
②代码
3.10 职责链模式(chain of responsibility):层层上报处理刑事案件
①解析
职责链模式是一种行为设计模式,定义了一系列对象,每个对象可以选择处理某个请求,也可以将该请求传给链中的下一个对象。
模式结构(职责链模式包含以下角色):
- 抽象处理器(Handler):定义出一个处理请求的接口。
- 具体处理器(ConcreteHandler):实现抽象处理器的接口,处理它所负责的请求。如果不处理该请求,则把请求转发给它的后继者。
- 客户端(Client):创建处理器对象,并将请求发送到某个处理器。
示例:
演示国家刑事案件处理流程,下级先处理,如果处理不了,交给下一个
- 定义Handler interface接口
- SetNext(handler Handler) //设置下一个处理器
- Handle(request int) //处理请求
- 定义type TownHandler struct
- NextHandler Handler //它的下一个处理器
- Handle(request int) :如果案件级别高于20,就交给下一handler处理
- func SetNext(handler Handler)
- 定义type CityHandler struct
- NextHandler Handler //它的下一个处理器
- Handle(request int) :如果案件级别高于100,就交给下一handler处理
- func SetNext(handler Handler)
- 定义type ProvinceHandler struct
- NextHandler Handler //它的下一个处理器
- Handle(request int)
- func SetNext(handler Handler)
②代码
3.11 访问者模式(visitor):dev、pro打印不同信息
①解析
访问者模式是一种操作一组对象的操作,它的目的是不改变对象的定义,但可以新增不同的访问者来定义新的操作。
访问者的核心思想是为了访问比较复杂的数据结构,不去改变原数据结构,而是把对数据的操作抽象出来,在访问的过程中以回调形式在访问者中处理逻辑操作。
如果要新增一组操作,那么只需要增加一个新的访问者。
示例:
根据不同环境打印不同内容,在生产环境和开发环境中打印处不同的内容
- 定义IVisitor interface
- Visit()
- 定义ProductionVisitor struct,实现IVisitor接口
- env string
- func Visit():判断env如果是生产环境,则打印生产环境的内容
- 定义DevelopmentVisitor struct,实现IVisitor接口
- env string
- func Visit():判断env如果是开发环境,则打印开发环境的内容
- 定义IElement interface接口
- func Accept(IVisitor)
- 定义Element struct,实现IElement接口
- visitors []IVisitor
- 定义ExampleLog struct(要打印的日志)
- Element
- 实现print方法,遍历visitors,调用每个visitor的Visit方法