1. iocgo簡介
習慣于Java或者C#開發的人應該對控制反轉與依賴注入應該再熟悉不過了,在Java平臺有鼎鼎大名的Spring框架,在C#平臺有Autofac,Unity,Windsor等,我當年C#開發時用的最多的就是Windsor,使用IoC容器是面向物件開發中非常方便的解耦模塊之間的依賴的方法,各個模塊之間不依賴于實作,而是依賴于介面,然后在建構式或者屬性或者方法中注入特定的實作,方便了各個模塊的拆分以及模塊的獨立單元測驗,
在[長安鏈]的設計中,各個模塊可以靈活組裝,模塊之間的依賴基于protocol中定義的介面,每個介面有一個或者多個官方實作,當然第三方也可以提供該介面更多的實作,為了實作更靈活的組裝各個模塊,管理各個模塊的依賴關系,于是我寫了iocgo這個輕量級的golang版Ioc容器,
2. iocgo如何使用
2.1 iocgo包的安裝
現在go官方版本已經出到1.17了,當然我在代碼中其實也沒有用什么新版本的新特性,于是就用1.15版本或者之后的Go版本即可,要使用iocgo包,直接通過go get添加到專案中:
go get github.com/studyzy/iocgo
2.2 使用示例與說明
2.2.1 最簡單的例子:
type Fooer interface {
Foo(int)
}
type Foo struct {
}
func (Foo)Foo(i int) {
fmt.Println("foo:",i)
}
type Barer interface {
Bar(string)
}
type Bar struct {
}
func (Bar) Bar(s string){
fmt.Println("bar:",s)
}
type Foobarer interface {
Say(int,string)
}
type Foobar struct {
foo Fooer
bar Barer
}
func NewFoobar(f Fooer,b Barer) Foobarer{
return &Foobar{
foo: f,
bar: b,
}
}
func (f Foobar)Say(i int ,s string) {
f.foo.Foo(i)
f.bar.Bar(s)
}
func TestContainer_SimpleRegister(t *testing.T) {
container := NewContainer()
container.Register(NewFoobar)
container.Register(func() Fooer { return &Foo{} })
container.Register(func() Barer { return &Bar{} })
var fb Foobarer
container.Resolve(&fb)
fb.Say(123,"Hello World")
}
這里我使用NewContainer()創建了一個新的容器,然后在容器中呼叫Register方法注冊了3個介面和對應的建構式,分別是:
- Foobarer介面對應NewFoobar(f Fooer,b Barer)建構式
- Fooer介面對應構造&Foo{}的匿名函式,
- Barer介面對應構造&Bar{}的匿名函式,
接下來呼叫Resolve函式,并傳入var fb Foobarer 這個介面變數的指標,iocgo就會自動去構建Foobarer對應的實體,并最終將實體賦值到fb這個變數上,于是最后我們就可以正常呼叫fb.Say實體方法了,
2.22. Register 的選項
iocgo的注冊interface到物件的函式定義如下:
func Register(constructor interface{}, options ...Option) error
iocgo為Register函式提供了以下引數選項可根據實際情況選擇性使用:
- Name 為某個interface->物件的映射命名
- Optional 表名這個建構式中哪些注入的interface引數是可選的,如果是可選,那么就算找不到interface對應的實體也不會報錯,
- Interface 顯式宣告這個建構式回傳的實體是映射到哪個interface,
- Lifestyle(isTransient) 宣告這個建構式在構造實體后是構造的臨時實體還是單例實體,如果是臨時實體,那么下次再獲取該interface對應的實體時需要再次呼叫建構式,如果是單例,那么就快取實體到容器中,下次再想獲得interface對應的實體時直接使用快取中的,不需要再次構造,
- DependsOn 這個主要是指定建構式中的某個引數在通過容器獲得對應的實體時,應該通過哪個Name去獲得對應的實體,
- Parameters 這個主要用于指定建構式中的某些非容器托管的引數,比如某建構式中有int,string等引數,而這些引數的實體是不需要通過ioc容器進行映射托管的,那么就在這里直接指定,
- Default 這個主要用于設定一個interface對應的默認的實體,也就是如果沒有指定Name的情況下,應該找哪個實體,
關于每一個引數該如何使用,我都寫了UT樣例,具體參考:
container_test.go
2.2.3. 注冊實體
如果我們已經有了某個物件的實體,那么可以將該實體和其想映射的interface直接注冊到ioc容器中,方便其他依賴的物件獲取,RegisterInstance函式定義如下:
RegisterInstance(interfacePtr interface{}, instance interface{}, options ...Option) error
使用上也很簡單,直接將實體對應的interface的指標作為引數1,實體本身作為引數2,傳入RegisterInstance即可:
b := &Bar{}
var bar Barer //interface
container.RegisterInstance(&bar, b) // register interface -> instance
2.2.4. 獲得實體
相關映射我們通過Register函式和RegisterInstance函式已經注冊到容器中,接下來就需要從容器獲得指定的實體了,獲得實體需要呼叫函式:
func Resolve(abstraction interface{}, options ...ResolveOption) error
這里第一個引數abstraction是我們想要獲取的某個interface的指標,第二個引數是可選引數,目前提供的選項有:
- ResolveName 指定使用哪個name的interface和實體的映射,如果不指定,那么就是默認映射,
- Arguments 指定在呼叫對應的建構式獲得實體時,傳遞的引數,比如int,string等型別的不在ioc容器中托管的引數,可以在這里指定,如果建構式本身需要這些引數,而且在前面Register的時候已經通過Parameters選項進行了指定,那么這里新的指定會覆寫原有Register的指定,
var fb Foobarer
err:=container.Resolve(&fb)
另外如果我們的建構式return的值中支持error,而且實際構造的時候確實回傳了error,那么Resolve函式也會回傳對應的這個err,
特別注意:Resolve的第一個引數是申明的某個interface的指標,一定要是指標,不能直接傳interface
2.2.5. 結構體引數和欄位填充
有些時候建構式的入參非常多,于是我們可以申明一個結構體,把所有入參都放入這個結構體中,這樣建構式就只需要一個引數了,iocgo也支持自動填充這個結構體中interface對應的實體,從而構造新的物件,另外iocgo也提供了Fill方法,可以直接填充某個結構體,比如:
type FoobarInput struct {
foo Fooer
bar Barer
msg string
}
input := FoobarInput{
msg: "studyzy",
}
container.Register(func() Fooer { return &Foo{} })
container.Register(func() Barer { return &Bar{} })
err := container.Fill(&input)
結構體中的欄位還支持tag,目前提供的tag有兩種:
- name //指定這個欄位在獲得對應的實體時使用的name
- optional //指定這個欄位是否是可選的,如果是,那么就算獲得不到對應的實體,也不會報錯,
示例example:
type FoobarInputWithTag struct {
foo Fooer `optional:"true"`
bar Barer `name:"baz"`
msg string
}
2.2.6. 函式呼叫
除了建構式注入之外,iocgo也支持函式注入,我們申明一個函式,這個函式的引數中有些引數是interface,那么通過呼叫iocgo中的Call方法,可以為這個函式注入對應的實體作為引數,并最終完成函式的呼叫,
示例 example:
func SayHi1(f Fooer, b Barer) {
f.Foo(1234)
b.Bar("hi")
}
Register(func() Fooer { return &Foo{} })
Register(func() Barer { return &Bar{} })
Call(SayHi1)
Call函式也是支持選項的,目前提供了2個選項:
- CallArguments 指定函式中某個引數的值
- CallDependsOn 指定函式中某個引數在通過ioc容器獲得實體時使用哪個name來獲得實體,
最后函式呼叫完成,如果函式本身有多個回傳值,有error回傳,那么Call函式也會回傳對應的結果,
2.3 參考:
在寫這個iocgo的代碼時,主要參考了以下兩個Ioc相關的專案:
- https://github.com/golobby/container
- https://github.com/castleproject/Windsor
3. 總結
iocgo是一個純Golang語言開發的用于管理依賴注入的IoC容器,使用這個容器可以很好的實作go語言下的面向物件開發,模塊解耦,現已經開源,歡迎大家使用,開源地址:https://github.com/studyzy/iocgo
【本文章出自博客園深藍居,轉載請注明作者出處,如果您覺得博主的文章對您有很大幫助,歡迎支付寶([email protected])對博主進行打賞,】轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/309383.html
標籤:Go
