您好,登錄后才能下訂單哦!
這篇文章主要介紹了go語言控制反轉指的是什么的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇go語言控制反轉指的是什么文章都會有所收獲,下面我們一起來看看吧。
在go語言中,控制反轉(IoC)是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度,就是代碼控制權從業務代碼“反轉”到框架代碼。常見的控制反轉方式叫做依賴注入,還有一種方式叫“依賴查找”;通過控制反轉,對象在被創建的時候,由一個調控系統內所有對象的外界實體將其所依賴的對象的引用傳遞給它。
控制反轉(Inversion of Control,縮寫為IoC),是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫“依賴查找”(Dependency Lookup)。通過控制反轉,對象在被創建的時候,由一個調控系統內所有對象的外界實體將其所依賴的對象的引用傳遞給它。也可以說,依賴被注入到對象中。
講得通俗一點,假如我有一個控制器,UserController,它可以Code,Read,Eat ,當然它還有隱式的__construct構造函數,__destruct析構函數,我們知道這些默認函數在特定的情景會自己觸發,比如初始化的時候,生命周期結束釋放資源的時候,但是我們如果假如這些函數本身都不會自己觸發,那么我們作為作者怎么去讓他執行。實際上我的控制器還有ArticleController ,YouBadBadController,我怎么去處理。
各干各的 User你干活之前先去構建一下自己,Article你干活之前也去構建一下自己 這個情況短板就很明顯了,后面介紹,每個控制器都要去各干各的,實際上都是Controller ,在處理公共行為的時候,其實我們可以借組外部實現和管理。 我們不用默認的魔法函數了,介紹一個具體場景,假如我現在需要每個控制器都要實現并調用一個handle函數。我們怎么合理去完成,假如現在還要執行一個run 方法 ,每個控制器添加完run函數之后,我們是不是還要寫他們的調度;
控制反轉統一管理 這個操作是不是可以讓一個公共的ControllerService幫忙handle就行了,我們現在不考慮繼承。
class ControllerService{
public functiondo(){
->handle();
} //去吧比卡丘; }
}
等等,小智不投精靈球怎么去吧,小智呢? 我們需要把控制方帶過來
class ControllerService{
public $handler;
public function __construct($handler){
$this->handler=$handler ;
} //通過構造函數帶入; }
//
public function setHandler($handler){
$this->handler->handle();
} //通過setter帶入; }
public function do(){
$this->handler->handle();
} //去吧比卡丘; }
}
new ControllerService()->setHandler(new UserController())->do();
這樣控制權已經反轉給ControllerService了;
Go語言中的interface 反射機制也是Ioc的體現
設計
采用的第三方庫
使用起來還是比較簡單的,無非就是RegisterTo, Invoke,但是任何的庫都需要結合框架起來才有意義。
一提到松耦合,在GO中很容易就想到接口(interface),所以我們用接口實現的各個層之間的松耦合。
按照傳統的MVC框架,一般服務端會有幾種分層,Controler層、Service層、Module層 從上到下,如何將Ioc結合在框架中才是值得探討的事情。
調用結構:由于沒有服務,main函數充當的是Controler、Service是服務層、Module是數據層、Resource是存儲層、app是各種接口的定義
main-->Service-->Module-->Resource
為了演示服務之間的調用,我們定義了service1和service2兩種服務
實現
package app
type Service1 interface {
AddData(string)
DelData(string)
}
type Service2 interface {
AddData(string)
DelData(string)
}
type Module interface {
DataToSave(string)
DataToRemove(string)
}
type Resource interface {
Save(string)
Remove(string)
}
package app
import (
"github.com/berkaroad/ioc"
"github.com/spf13/viper"
)
func GetOrCreateRootContainer() ioc.Container {
v := viper.Get("runtime.container")
if v == nil {
v = ioc.NewContainer()
viper.Set("runtime.container", v)
}
return v.(ioc.Container)
}
這里其實怎么實現都行,只是一個單例NewContainer就可以
package resource
import (
"fmt"
"github.com/berkaroad/ioc"
"github.com/zhaoshoucheng/hodgepodge/IoC/app"
)
type ResourceObj struct {
name string
}
func (r *ResourceObj) Save(str string) {
fmt.Println(r.name, " Save ", str)
}
func (r *ResourceObj) Remove(str string) {
fmt.Println(r.name, " Remove ", str)
}
func init() {
mo := &ResourceObj{name: "mongo"}
// static assert 靜態斷言類型檢測
func(t app.Resource) {}(mo)
app.GetOrCreateRootContainer().RegisterTo(mo, (*app.Resource)(nil), ioc.Singleton)
//rd := &ResourceObj{name: "redis"} 實現是用的map,所以mong會被覆蓋
//app.GetOrCreateRootContainer().RegisterTo(rd, (*app.Resource)(nil), ioc.Singleton)
}
RegisterTo是注冊過程,在mo對象后續會當作app.Resource接口的實現來使用,其底層實現是一個map
package module
import (
"fmt"
"github.com/berkaroad/ioc"
"github.com/zhaoshoucheng/hodgepodge/IoC/app"
)
var (
rs app.Resource
)
type ModuleObj struct {
}
func (mo *ModuleObj) DataToSave(str string) {
fmt.Println("ModuleObj DataToSave ", str)
rs.Save(str)
}
func (mo *ModuleObj) DataToRemove(str string) {
fmt.Println("ModuleObj DataToRemove ", str)
rs.Remove(str)
}
func init() {
mo := &ModuleObj{}
// static assert 靜態斷言類型檢測
func(t app.Module) {}(mo)
app.GetOrCreateRootContainer().RegisterTo(mo, (*app.Module)(nil), ioc.Singleton)
app.GetOrCreateRootContainer().Invoke(func(r app.Resource) {
rs = r
})
}
因為我們之前app.Resource已經注冊過,所以這里Invoke的時候就可以獲取到實現該接口的對象
package service
import (
"fmt"
"github.com/berkaroad/ioc"
"github.com/zhaoshoucheng/hodgepodge/IoC/app"
)
var (
module app.Module
service2 app.Service2
)
type Service1 struct {
}
func (s1 *Service1) AddData(str string) {
service2.AddData(str)
fmt.Println("Service1 AddData ", str)
module.DataToSave(str)
}
func (s1 *Service1) DelData(str string) {
service2.DelData(str)
fmt.Println("Service1 DelData ", str)
module.DataToRemove(str)
}
func init() {
s1 := &Service1{}
s2 := &Service2{}
service2 = s2
//static assert 靜態斷言做類型檢查
func(t app.Service1) {}(s1)
func(t app.Service2) {}(s2)
app.GetOrCreateRootContainer().RegisterTo(s1, (*app.Service1)(nil), ioc.Singleton)
app.GetOrCreateRootContainer().RegisterTo(s2, (*app.Service2)(nil), ioc.Singleton)
app.GetOrCreateRootContainer().Invoke(func(mod app.Module) {
module = mod
})
}
package main
import (
"github.com/zhaoshoucheng/hodgepodge/IoC/app"
_ "github.com/zhaoshoucheng/hodgepodge/IoC/resource"
_ "github.com/zhaoshoucheng/hodgepodge/IoC/module"
_ "github.com/zhaoshoucheng/hodgepodge/IoC/service"
)
func main() {
var s1 app.Service1
app.GetOrCreateRootContainer().Invoke(func(service app.Service1) {
s1 = service
})
s1.AddData("IOC Test")
}
思考
我們為什么要用到Ioc呢?個人感覺有幾點好處
1.解決各種依賴問題,寫GO可能都遇到過循環引用問題,越是復雜的系統就越有可能出現這種混亂的調用現象。
2.實現了很好的擴展性,如果存儲層想從redis切換到mongo,定義一個相同的對象,替換注冊對象就可以輕松實現。
3.易使用,隨時隨地可以通過Invoke獲取相應的接口對象。
問題
難道就沒有問題嗎?
當然有,就是引用順序的問題,也就是先register 還是先invoke 這個在例子中感覺很簡單,但是在工程中很容易出錯
_ "github.com/zhaoshoucheng/hodgepodge/IoC/module"
_ "github.com/zhaoshoucheng/hodgepodge/IoC/resource"
_ "github.com/zhaoshoucheng/hodgepodge/IoC/service"
_ "github.com/zhaoshoucheng/hodgepodge/IoC/resource"
_ "github.com/zhaoshoucheng/hodgepodge/IoC/module"
_ "github.com/zhaoshoucheng/hodgepodge/IoC/service"
第一種寫法就會崩潰,第二種正確
原因第一種module 的init 先執行,app.Resource的對象還沒有注冊。所以init的先后順序很重要
但這個是憑借字節碼進行的排序,有時IDE還不讓我們改,所以需要一些控制器去處理這種情況。
關于“go語言控制反轉指的是什么”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“go語言控制反轉指的是什么”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。