什么是可插拔式框架

最早接触可插拔式的框架是python的flask,当时觉得这玩意老牛逼了,任何组件都能替换,你可以用jinja2/mako来做模版引擎,你可以选择SQLAlchemy或者其他orm框架来操作数据库。

思考了很久,pluggable的本质在于可替换。可替换在软件开发处处都可以体现,比如mysql驱动和mysql驱动实现,orm框架和数据库连接池,同样的orm框架,可以使用不同的连接池。而这种可替换在于抽象,golang标准库给我们提供了一套标准的sql操作接口,不同数据库只要实现这个接口。

这样想,pluggable也是离不开抽象的。抽空研究了一下micro的代码,发现,确实如此。

可默认,可替换

1. 默认参数

pluggable,最直观的理解大概就是“可以替换,不替换也行”。在编码中,很多语言天生有一项能力和这个类似——默认参数:

# python
def test(name="lqczzz"):
    print(name)

test()  # "lqczzz"
test("jack")  # "jack"

这里,name这个变量是可以替换的,某种意义上也可以说是“pluggable”

2. 可选参数 + 默认值

默认参数很方便,golang没有默认参数,可以通过可选参数和默认值来实现

const DefaultName = "lqczzz"

func test(args ...string) {
    name := DefaultName
    if len(args) != 0 { name = args[0] }

    fmt.Println(name)
}

// 不传参数
test() // "lqczzz"

// 传参数
test("jack") // "jack"

micro的可插拔式的实现本质上也是如此。

// eg:
type iBE interface {
    FeatureImpl()
}

type beOption struct {
    pm iPM
}

type qczzzl struct {
    opts beOption
}

func (qc *qczzzl) FeatureImpl() {
    qc.opts.pm.AddFeature()
    fmt.Println("qczzzl will implement it!")
}

pm = &defaultPM{}
be = &qczzzl{opts: beOption{pm: pm}}
be.FeatureImpl()
// output:
// zhangiaolong add feature
// qczzzl will implement it!

// 换pm
newPm := &pmLiyunlong{}
be.opts.pm = newPm 
be.FeatureImpl()
// output:
// liyunlong add feature
// qczzzl will implement it!

3. 接口

函数式参数

模块的抽象

变化和不变

接口
契约不变
实现方式变化

struct
封装依赖的变化(不变的契约依赖)
封装通用数据(不变的数据依赖)