在go語言中,依賴注入(DI)是一種解耦元件之間依賴關係的設計模式;在需要的時候,不同元件之間可以透過一個統一的介面來取得其它元件中的對象和狀態。依賴注入的好處是解耦;而解耦能帶來更多的好處:程式碼擴展性增強,程式碼的可維護性增強,更容易進行單元測試等等。
本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。
第一次聽到這個字的時候我是一臉懵逼的,很拗口有沒有,可能很多學過spring的同學覺得這是很基礎很好理解的知識,但因為我之前沒學過Java和spring,所以第一次接觸這個字的時候是很懵的。
依賴注入,英文名dependency injection,簡稱DI。依賴兩個字很好理解,在軟體設計上,從架構模組到函數方法都存在大大小小的依賴關係。
比如說在new A 之前需要先new B ,A依賴B,這時候我們就可以說B是A的依賴,A控制B,AB之間存在著耦合的關係,而程式碼設計思想是最好可以做到鬆散耦合。如果某一天B需要改造那麼A也需要跟著改造。這是一個依賴你可以覺得沒問題,但如果是A->B->C->D->E->F之間存在一連串的依賴關係,那麼改造起來就會十分麻煩。
這時候就需要一種東西來解開他們之間的強耦合,怎麼解耦呢,只能藉由第三方力量了,我們把A對B的控制權交給第三方,這種想法就稱為控制反轉(IOC Inversion Of Control),這個第三方稱為IOC容器。而IOC容器要做的事情就是new一個B出來,然後把這個B的實例注入到A裡面去,然後A就可以正常的使用基於B的方法了,這個過程被稱為依賴項注入,而基於IOC的這種方法就叫做依賴注入。
簡單來說,依賴注入(DI)是一種解耦元件之間依賴關係的設計模式。在需要的時候,不同組件之間可以透過一個統一的介面來取得其它組件中的物件和狀態。 Go語言的介面設計,避免了許多需要使用第三方依賴注入框架的情況(例如Java,等等)。我們的注入方案只提供非常少的類似Dager或Guice中的注入方案,而專注於盡量避免手動去配置物件和元件之間的依賴關係。
明白了依賴注入的思想,也應該明白了其帶來的最大好處-解耦。
而解耦又能帶來更多的好處:程式碼擴展性增強,程式碼的可維護性增強,更容易進行單元測試等等。
那麼依賴注入如何實現呢?
Java中有以下幾種方式:
setter方法注入:實作特定屬性的public set方法,來讓外部容器呼叫傳入所依賴類型的對象。
基於介面的注入:實作特定介面以供外部容器注入所依賴類型的物件。
基於建構函數的注入:實作特定參數的建構函數,在新物件時傳入所依賴類型的物件。
基於註解的注入:在程式碼裡加上特定的關鍵字實作注入。
註解是最常見的方式,它像註解一樣不被當作程式碼來執行,而是專門供別人閱讀。但註釋的讀者完全是人類,而註解的主要讀者除了人類之外還有框架或預編譯器。
wire就是基於註解的依賴注入方式。 wire
是Google 開源的依賴注入工具,我們只需要在一個特殊的go
檔案中告訴wire
類型之間的依賴關係,它會自動幫我們產生程式碼,幫助我們建立指定類型的對象,並組裝它的依賴。
wire
有兩個基礎概念,Provider
(建構器)和Injector
(注入器)。
透過提供provider
函數,讓wire
知道如何產生這些依賴物件。 wire
根據我們定義的injector
函數簽名,產生完整的injector
函數,injector
函數是最終我們需要的函數,它將按依賴順序呼叫provider
。
wire
的要求很簡單,新建一個wire.go
檔案(檔案名稱可以隨意),建立我們的初始化函數。例如,我們要建立並初始化一個Mission
對象,我們就可以這樣:
//+build wireinject package main import "github.com/google/wire" func InitMission(name string) Mission { wire.Build(NewMonster, NewPlayer, NewMission) return Mission{} }
可以看到第一行的註解: build wireinject,表示這是一個注入器。 build
其實是 Go 語言的特性。類似 C/C 的條件編譯,在執行go build
時可傳入一些選項,根據這個選項決定某些檔案是否編譯。 wire
工具只會處理有wireinject
的文件,所以我們的wire.go
文件要加上這個。
在函數中,我們呼叫wire.Build()
將建立Mission
所依賴的類型的建構器傳進去。例如,需要呼叫NewMission()
建立Mission
類型,NewMission()
接受兩個參數一個Monster
類型,一個 Player
類型。 Monster
類型物件需要呼叫NewMonster()
創建,Player
類型物件需要呼叫NewPlayer()
創建。所以NewMonster()
和NewPlayer()
我們也需要傳給wire
。
寫完wire.go檔之後執行wire指令,就會自動產生一個wire_gen.go檔。
// Code generated by Wire. DO NOT EDIT. //go:generate wire //+build !wireinject package main // Injectors from wire.go: func InitMission(name string) Mission { player := NewPlayer(name) monster := NewMonster() mission := NewMission(player, monster) return mission }
可以看到wire自動幫我們產生了InitMission方法,此方法中依序初始化了player,monster和mission。之後在我們的main函數中就只需呼叫這個InitMission即可。
func main() { mission := InitMission("dj") mission.Start() }
而在沒用過依賴注入之前,我們的程式碼是這樣的:
func main() { monster := NewMonster() player := NewPlayer("dj") mission := NewMission(player, monster) mission.Start() }
是不是簡潔了很多。這裡只有三個物件的初始化,如果更多可能才會意識到依賴注入的好處。
例如:
wire.go文件: // +build wireinject // The build tag makes sure the stub is not built in the final build. package di import ( "github.com/google/wire" ) //go:generate kratos t wire func InitApp() (*App, func(), error) { panic(wire.Build(dao.Provider, service.Provider, http.New, grpc.New, NewApp)) } 实现文件: //dao var Provider = wire.NewSet(New, NewDB, NewRedis) //service var Provider = wire.NewSet(New, wire.Bind(new(pb.Server), new(*Service))) 生成的wire_gen.go 文件: func InitApp() (*App, func(), error) { redis, cleanup, err := dao.NewRedis() if err != nil { return nil, nil, err } db, cleanup2, err := dao.NewDB() if err != nil { cleanup() return nil, nil, err } daoDao, cleanup3, err := dao.New(redis, db) if err != nil { cleanup2() cleanup() return nil, nil, err } serviceService, cleanup4, err := service.New(daoDao) if err != nil { cleanup3() cleanup2() cleanup() return nil, nil, err } engine, err := http.New(serviceService) if err != nil { cleanup4() cleanup3() cleanup2() cleanup() return nil, nil, err } server, err := grpc.New(serviceService) if err != nil { cleanup4() cleanup3() cleanup2() cleanup() return nil, nil, err } app, cleanup5, err := NewApp(serviceService, engine, server) if err != nil { cleanup4() cleanup3() cleanup2() cleanup() return nil, nil, err } return app, func() { cleanup5() cleanup4() cleanup3() cleanup2() cleanup() }, nil }
所以,依賴注入到底是什麼?
封裝解耦罷了。
以上是go語言依賴注入是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!