单例模式 单例模式是用来控制类型实例的数量的,当需要确保一个类型只有一个实例时,就需要使用单例模式。
饿汉模式 该模式适用于在程序早期初始化时创建已经确定需要加载的类型实例,比如项目的数据库实例。 Go 语言实现时,借助 Go 的init
函数来实现特别方便,虽然 init()
函数在每个 package 中都被执行了多次,但是在同一个 package 中全局变量只会被初始化一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package daotype databaseConn struct { ... } var dbConn *databaseConnfunc init () { dbConn = &databaseConn{} } func GetInstance () *databaseConn{ return dbConn }
C++实现方式:
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 ``` ### 懒汉模式 该模式就是延迟加载,要考虑并发环境下,你的判断实例是否已经创建时,是不是用的当前读。 ```go package singleton import ( "sync" ) type singleton struct {}var instance *singleton var once sync.Once func GetInstance () *singleton { once.Do (func () { instance = &singleton{} }) }
策略模式 定义一类算法族,将每个算法分别封装起来,让他们可以互相替换,此模式让算法的变化独立于使用算法的客户端。
例子:用户支付,客户端使用微信支付、或者是其他三方在线支付。
PayBehavior:抽象策略,对支付任务进行接口抽象
WxPay 和 ThirdPay :是具体的策略实现
PaxCtx:上下文对象在这里有两个作用,第一是协调自己持有的 PayBehavior 具体实现,完成支付的任务,第二是维护发起支付需要的支付参数–即图中的私有属性payParams
。
1 2 3 4 type PayBehavior interface { OrderPay(px *PayCtx) }
有了接口后,我们来定义两个策略的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 type WxPay struct {}func (*WxPay) OrderPay(px *PayCtx) { fmt.Printf("Wx支付加工支付请求 %v\n" , px.payParams) fmt.Println("正在使用Wx支付进行支付" ) } type ThirdPay struct {}func (*ThirdPay) OrderPay(px *PayCtx) { fmt.Printf("三方支付加工支付请求 %v\n" , px.payParams) fmt.Println("正在使用三方支付进行支付" ) }
有了策略的实现后,还得有个上下文来协调它们,以及持有完成这个任务所必需的那些入参payParams
:
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 type PayCtx struct { payBehavior PayBehavior payParams map [string ]interface {} } func (px *PayCtx) setPayBehavior(p PayBehavior) { px.payBehavior = p } func (px *PayCtx) Pay() { px.payBehavior.OrderPay(px) } func NewPayCtx (p PayBehavior) *PayCtx { params := map [string ]interface {}{ "appId" : "234fdfdngj4" , "mchId" : 123456 , } return &PayCtx{ payBehavior: p, payParams: params, } }
调用过程:
1 2 3 4 5 6 7 8 9 10 func main () { wxPay := &WxPay{} px := NewPayCtx(wxPay) px.Pay() thPay := &ThirdPay{} px.setPayBehavior(thPay) px.Pay() }
责任链模式 它是一种行为型设计模式。使用这个模式,我们能为请求创建一条由多个处理器组成的链路,每个处理器各自负责自己的职责,相互之间没有耦合,完成自己任务后请求对象即传递到链路的下一个处理器进行处理。
责任链在很多流行框架里都有被用到,像中间件、拦截器等框架组件都是应用的这种设计模式。
背景 上面我们说了职责链在项目公共组件中的一些应用,让我们能在核心逻辑的前置和后置流程中增加一些基础的通用功能。但其实在一些核心的业务中,应用职责链模式能够让我们无痛地扩展业务流程的步骤 。
比如淘宝在刚刚创立的时候购物生成订单处理流程起初可能是这样的。
整个流程比较干净**”用户参数校验–购物车数据校验–商品库存校验–运费计算–扣库存—生成订单”**,我们姑且把它称为清纯版的购物下单流程,这通常都是在产品从0到1的时候,流程比较清纯,在线购物你能实现在线选品、下单、支付这些就行了。
责任链模式抽象成 UML 类图如下:
具体实现 下单接口结构如下:
1 2 3 4 5 type OrderHandler interface { SetNext(OrderHandler) OrderHandler Execute(*order) error Do() error }
成员方法
SetNext
: 把下一个对象的实例绑定到当前对象的nextHandler
属性上;
Do
: 当前对象业务逻辑入口,他是每个处理对象实现自己逻辑的地方;
Execute
: 负责职责链上请求的处理和传递;它会调用当前对象的Do
,nextHandler
不为空则调用nextHandler.Do
;
Next类充当抽象类型,实现公共方法(SetNext和Execute),抽象方法(Do)不实现留给实现类自己实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 type Next struct { nextHandler OrderHandler } func (n *Next) SetNext(handler OrderHandler) OrderHandler { n.nextHandler = handler return handler } func (n *Next) Execute(o *order) (err error ) { if n.nextHandler != nil { if err = n.nextHandler.Do(); err != nil { return } return n.nextHandler.Execute(o) } return }
成员属性
nextHandler
: 下一个等待被调用的对象实例
具体流程实现结构体如下:
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 type DataCheck struct { Next } func (m *DataCheck) Do() (err error ) { fmt.Println("doing UserDataCheck" ) return } type FreightCalc struct { Next } func (m *FreightCalc) Do() (err error ) { fmt.Println("doing FreightCalc" ) return } type DecreaseRepe struct { Next } func (m *DecreaseRepe) Do() (err error ) { fmt.Println("doing DecreaseRepe" ) return } type StartHandler struct { Next } func (h *StartHandler) Do(c *order) (err error ) { return } func main () { startHandler := &StartHandler{} startHandler.SetNext(&DataCheck{}). SetNext(&FreightCalc{}). SetNext(&DecreaseRepe{}) o := &order{name: "abc" } startHandler.Execute(o) fmt.Println("end" ) } type order struct { name string }
代理模式 代理模式 是一种结构型设计模式。 其中代理控制着对于原对象的访问, 并允许在将请求提交给原对象的前后进行一些处理,从而增强原对象的逻辑处理。
进行逻辑处理的原对象通常被称作服务对象,代理要跟服务对象实现相同的接口 ,才能让客户端傻傻分不清自己使用的到底是代理还是真正的服务对象,这样一来代理就能在客户端察觉不到的情况下对服务对象的处理逻辑进行增强。
UML图如下:
具体实现 假设有一个代表小汽车的 Car 类型,小汽车要的主要行为就是可以让人驾驶,所以 Car 需要实现一个代表驾驶行为的接口(interface)Vehicle,该接口只有一个方法Drive()
1 2 3 4 5 6 7 8 9 10 11 type Vehicle interface { Drive() } type Car struct {}func (c *Car) Drive() { fmt.Println("Car is being driven" ) }
果在开车时要加一个驾驶员的年龄限制,我们该怎么办呢? 通常的做法是,加一个表示驾驶员的类型 Driver
。
1 2 3 type Driver struct { Age int }
然后再来一个包装 Driver 和 Vehicle 类型的包装类型,也就是所谓的代理类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 type CarProxy struct { vehicle Vehicle driver *Driver } func NewCarProxy (driver *Driver) *CarProxy { return &CarProxy{&Car{}, driver} } func (c *CarProxy) Drive() { if c.driver.Age >= 16 { c.vehicle.Drive() } else { fmt.Println("Driver too young!" ) } }
调用:
1 2 3 4 5 6 func main () { car := NewCarProxy(&Driver{12 }) car.Drive() car2 := NewCarProxy(&Driver{22 }) car2.Drive() }