go语言中控制反转是什么
在go语言中,控制反转(IoC)是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度,就是代码控制权从业务代码“反转”到框架代码。常见的控制反转方式叫做依赖注入,还有一种方式叫“依赖查找”;通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。
本教程操作环境:windows7系统、GO 1.18版本、Dell G3电脑。
控制反转是什么
控制反转(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的体现
Golang 控制反转 (IOC)在工程中应用
设计
采用的第三方库:https://github.com/berkaroad/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) }
IOC 初始化
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 }) }
Main
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语言中控制反转是什么的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

在Go中安全地读取和写入文件至关重要。指南包括:检查文件权限使用defer关闭文件验证文件路径使用上下文超时遵循这些准则可确保数据的安全性和应用程序的健壮性。

如何为Go数据库连接配置连接池?使用database/sql包中的DB类型创建数据库连接;设置MaxOpenConns以控制最大并发连接数;设置MaxIdleConns以设定最大空闲连接数;设置ConnMaxLifetime以控制连接的最大生命周期。

Golang和C++分别是垃圾回收和手动内存管理编程语言,语法和类型系统各异。Golang通过Goroutine实现并发编程,C++通过线程实现。Golang内存管理简单,C++性能更强。实战案例中,Golang代码更简洁,C++性能优势明显。

Go框架架构的学习曲线取决于对Go语言和后端开发的熟悉程度以及所选框架的复杂性:对Go语言的基础知识有较好的理解。具有后端开发经验会有所帮助。复杂性不同的框架导致学习曲线差异。

在Golang中生成列表随机元素的方法:使用rand.Intn(len(list))生成列表长度范围内的随机整数;用该整数作为索引,从列表中获取相应元素。

Go框架凭借高性能和并发性优势脱颖而出,但也存在一些缺点,如相对较新、开发者生态系统较小、缺少某些功能。此外,快速变化和学习曲线可能因框架而异。Gin框架以其高效路由、内置JSON支持和强大的错误处理而成为构建RESTfulAPI的热门选择。

最佳实践:使用明确定义的错误类型(errors包)创建自定义错误提供更多详细信息适当记录错误正确传播错误,避免隐藏或抑制根据需要包装错误以添加上下文

Golang框架的优势Golang是一种高性能、并发编程语言,特别适用于微服务和分布式系统。 Golang框架通过提供一组现成的组件和工具,使开发这些应用程序变得更加容易。以下是Golang框架的一些关键优势:1.高性能和并发性:Golang本身以其高性能和并发性着称。它使用goroutine,这是一种轻量级的线程机制,允许并发执行代码,从而提高应用程序的吞吐量和响应能力。 2.模块化和可重用性:Golang框架鼓励模块化和可重用代码。通过将应用程序分解为独立的模块,您可以轻松维护和更新代
