Home >Backend Development >Golang >What is inversion of control in go language
In the Go language, Inversion of Control (IoC) is a design principle in object-oriented programming. It can be used to reduce the coupling between computer codes, that is, the code control is "reversed" from the business code. ” to the framework code. A common method of inversion of control is called dependency injection, and another method is called "dependency lookup"; through inversion of control, when an object is created, an external entity that controls all objects in the system references the objects it depends on. pass it.
The operating environment of this tutorial: Windows 7 system, GO version 1.18, Dell G3 computer.
Inversion of Control (Inversion of Control, abbreviated as IoC) is a design principle in object-oriented programming that can be used Reduce coupling between computer codes. The most common method is called Dependency Injection (DI), and the other method is called Dependency Lookup. Through inversion of control, when an object is created, an external entity that controls all objects in the system passes the reference of the object it depends on to it. It can also be said that dependencies are injected into the object.
To put it more simply, if I have a controller, UserController, it can Code, Read, and Eat. Of course, it also has implicit __construct constructor and __destruct destructor. We know these default functions. It will trigger itself in certain situations, such as initialization, when resources are released at the end of the life cycle, but if we assume that these functions themselves will not trigger themselves, then how can we, as authors, let them execute. In fact, my controller also has ArticleController and YouBadBadController. How should I handle it?
Each user should build himself before working. Article should also build himself before working. The shortcomings of this situation are obvious. As will be introduced later, each controller must be built. Everyone does their own thing, but they are actually all Controllers. When dealing with public behaviors, we can actually implement and manage them externally. We don't need the default magic function. Let's introduce a specific scenario. Suppose I now need each controller to implement and call a handle function. How can we complete it reasonably? If we still need to execute a run method now, after adding the run function to each controller, do we still need to write their schedule?
Is inversion of control a unified management of this operation? You can just let a public ControllerService help handle it. We are not considering inheritance now.
class ControllerService{ public functiondo(){ ->handle(); } //去吧比卡丘; } }
Wait a minute, how can Xiaozhi go without throwing a Poké Ball? Where is Xiaozhi? We need to bring the controller over
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();
so that the control has been reversed to ControllerService;
The interface reflection mechanism in Go language is also the embodiment of Ioc
Design
The third-party library used:https: //github.com/berkaroad/ioc
It is relatively simple to use, nothing more than RegisterTo and Invoke, but any library needs to be combined with a framework to be meaningful.
When it comes to loose coupling, it is easy to think of interface(interface) in GO, so we use interfaces to achieve loose coupling between various layers.
According to the traditional MVC framework, there are generally several layers on the server side, Controller layer, Service layer, and Module layer. From top to bottom, how to combine Ioc in the framework is something worth exploring.
Calling structure: Since there is no service, the main function acts as the Controller, Service is the service layer, and Module is the data Layer, Resource is the storage layer, and app is the definition of various interfaces
main-->Service-->Module-->Resource
In order to demonstrate the calls between services, we defined service1 and service2 Two services
implementation
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) }
In fact, no matter how you implement it, it is just a single instance 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 is the registration process. The mo object will be used as the implementation of the app.Resource interface later. Its underlying implementation is a 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 }) }
Because we have already registered app.Resource before, we can get the object that implements this interface when Invoke here
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") }
##Thoughts
我们为什么要用到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还不让我们改,所以需要一些控制器去处理这种情况。
The above is the detailed content of What is inversion of control in go language. For more information, please follow other related articles on the PHP Chinese website!