En langage Go, l'inversion de contrôle (IoC) est un principe de conception en programmation orientée objet, qui peut être utilisé pour réduire le couplage entre les codes informatiques. Cela signifie que le contrôle du code est « inversé » du code métier au code framework. . Une méthode courante d'inversion de contrôle est appelée injection de dépendances, et une autre méthode est appelée « recherche de dépendances » par inversion de contrôle. Lorsqu'un objet est créé, une entité externe qui contrôle tous les objets du système fait référence aux objets dont il dépend. passe-le.
L'environnement d'exploitation de ce tutoriel : système Windows 7, GO version 1.18, ordinateur Dell G3.
L'inversion de contrôle (en abrégé IoC) est un principe de conception en programmation orientée objet qui peut être utilisé pour réduire le couplage entre des codes informatiques. La méthode la plus courante est appelée Dependency Injection (DI), et l'autre méthode est appelée "Dependency Lookup". Par inversion de contrôle, lorsqu'un objet est créé, une entité externe qui contrôle tous les objets du système lui transmet la référence de l'objet dont elle dépend. On peut aussi dire que des dépendances sont injectées dans l'objet.
Pour le dire plus simplement, si j'ai un contrôleur, UserController, il peut coder, lire et manger. Bien sûr, il a également un constructeur __construct et un destructeur __destruct implicites. Nous savons que ces fonctions par défaut sont utilisées dans des situations spécifiques. Il se déclenchera tout seul, par exemple lors de l'initialisation, lorsque les ressources seront libérées à la fin du cycle de vie, mais si nous supposons que ces fonctions elles-mêmes ne se déclencheront pas d'elles-mêmes, alors comment pouvons-nous, en tant qu'auteurs, les laisser s'exécuter. En fait, mon contrôleur possède également ArticleController et YouBadBadController. Comment dois-je le gérer ?
Les utilisateurs font chacun leur propre travail. Vous devez vous construire avant de travailler. Les inconvénients de cette situation sont évidents, comme nous le verrons plus tard, chaque contrôleur doit faire son propre travail. sont en fait des contrôleurs. Lorsqu'il s'agit de comportements publics, nous pouvons réellement les mettre en œuvre et les gérer en externe. Nous n'avons pas besoin de la fonction magique par défaut. Introduisons un scénario spécifique. Supposons que j'ai maintenant besoin que chaque contrôleur implémente et appelle une fonction handle. Comment pouvons-nous le compléter raisonnablement ? Si nous devons encore exécuter une méthode run maintenant, après avoir ajouté la fonction run à chaque contrôleur, devons-nous toujours écrire leur planning
Est-il possible d'utiliser une inversion publique de contrôle pour gérer ? cette opération de manière uniforme ? Utilisez simplement le ControllerService pour vous aider avec la gestion. Nous n'envisageons pas l'héritage pour le moment.
class ControllerService{ public functiondo(){ ->handle(); } //去吧比卡丘; } }
Attendez une minute, comment Xiaozhi peut-il y aller sans lancer la Poké Ball ? Où est Xiaozhi ? Nous devons amener le contrôleur
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();
De cette façon, le contrôle a été inversé vers le ControllerService ;
Le mécanisme de réflexion de l'interface dans le langage Go est également l'incarnation d'Ioc
Design
La bibliothèque tierce utilisée : https://github.com/berkaroad/ioc
C'est relativement simple à utiliser, rien de plus que RegisterTo et Invoke, mais toute bibliothèque doit être combinée avec le framework. Cela a du sens.
Quand il s'agit de couplage lâche, il est facile de penser à une interface dans GO, nous utilisons donc des interfaces pour obtenir un couplage lâche entre différentes couches.
Selon le framework MVC traditionnel, il existe généralement plusieurs couches côté serveur, telles que la couche contrôleur, la couche service et la couche module de haut en bas, comment intégrer Ioc dans le framework mérite d'être exploré.
Structure d'appel : puisqu'il n'y a pas de service, la fonction principale fait office de contrôleur, le service est la couche de service, le module est la couche de données, la ressource est la couche de stockage et l'application est la définition. de diverses interfaces
main-- >Service-->Module-->Resource
Afin de démontrer les appels entre services, nous avons défini deux services, service1 et service2
implémentation
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) }
En fait, peu importe la façon dont vous l'implémentez ici, une seule instance de NewContainer fera l'affaire
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 est l'enregistrement processus, et l'objet mo sera traité comme app plus tard. L'implémentation de l'interface Resource est utilisée. Son implémentation sous-jacente est une 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 }) }
Parce que nous avons déjà enregistré app.Resource auparavant, donc quand nous En invoquant ici, nous pouvons obtenir l'objet qui implémente cette interface
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") }
Thinking
我们为什么要用到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还不让我们改,所以需要一些控制器去处理这种情况。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!