Go语法简略 - 依赖注入

通过对web应用框架背后原理的探索,引入了依赖注入的概念。如果你需要读懂或者写一个框架的话,依赖注入的思想绝对能帮到你。本文记录对依赖注入的探索。

区分依赖和宿主

现在我们把问题简化一下:

package main

import "fmt"

func MethodA(name string, f funca intb )) {
  fmt.Println("Enter MethodA:"name)
  f(3030"zdd") // 给f注入参数
  "Exit MethodA:")
}

MethodB) abmain() d := MethodB
  "zddhub"d}

在上述的例子中,为了完成MethodA的功能,需要依赖MethodB方法的实现,把MethodB方法叫做MethodA的依赖(Dependency),MethodA叫做宿主(Host)。上面的例子直接在Host中调用f(3030,"zdd")来给依赖注入参数。或者换一个角度理解,将自定义的方法MethodB整体注入到MethodA的执行环境中。为了通用性考虑,通常会实现一个依赖注入器。

依赖注入器

接下来实现一个依赖注入器(Injector)。在web应用框架的例子中,我们发现,在Host中,只能拿到依赖方法的参数类型,而我们需要将参数类型和参数值建立联系,因此,在Injector中封装一个map[Type]Value是自然而然的选择。

import ( "fmt" "reflect" ) type Injector struct mappers map[reflectType]Value // 根据类型map实际的值 func inj *Injector) SetMapvalue interface{}) injmappersTypeOfvalue)] = ValueOfGett Value { return t] Invokei []t i) if Kind() != Func { panic"Should invoke a function!") } inValues := make([]ValueNumIn()) for k := 0; k < (); k++ { inValuesk] In)) ret ).Callret Host"Enter Host:") // 利用注入器中的环境调用f // 这种使用方法,看起来就像把自定义的方法f里的执行语句放在Host中执行一样自然 // 语句从f里穿透到Host方法中,这就是注入一词的由来。 "Exit Host:"Dependency"Dependency: "var Injector // 全局的注入器,保存注入环境 { // 创建注入器 inj = &{()} Dependency 8080"www.zddhub.com""website" 这样,一个简易的注入器就完成了。突然发现它和装饰器有那么一丢丢的像呀。不断的在外围裹上一层层的衣服,而你所需要专心关注的,正是衣服里面的,啊哈哈。。。。

稍微改进一下我们的注入器,让它更通用些。

NewInjector return )} { // 让注入的方法不受限制 // 利用注入器中的环境调用f Injector () "zddhub@gmail.com"(10.24"email"email money float64number emailmoneynumber}) "Get""Hello world!"}) }

依赖注入的另一种使用场景

依赖注入的另一种使用方法是用在依赖属性的构造上,通常在测试框架中应用广泛。通过下面的例子,理解一下吧。

Camera // 定义一个单反相机
  Name string
  L    Lens
Lens { 
  LensType int // 0: 普通镜头,1: 广角镜头,2: 长焦镜头
c CameraCapture// 使用不同的镜头拍照
  m []3m[],152)">12= "normal lens""wide-angle lens""telephoto lens"

  value cL))
  index int
  Invalid index Interface().(LensLensType
  } else = 0
  "capture with"index])
// 持有一个全局的注入器,复用之前实现的注入器。

()

  c {"ZddCamera"{}} // 组装一个普通镜头的单反相机,通常组装一个单反需要很长的时间,假设你有工匠情结,并且DIY成功的概率是100%,努力工作一个月就可以组装完一台。
  () // 拍照
  // 如果想继续测试广角镜头和长焦镜头的拍照效果,那么是不是要这样?
  // d := Camera{"ZddCamera",Lens{1}} // 你又努力工作了一个月
  // e := Camera{"ZddCamera",Lens{2}} // 你又努力工作了一个月
  // 如果Boss告诉你,最近又进口了十几款镜头,你是不是感觉要死的心都有了呢。

  // 正常人只需要组装新的镜头
  wLens } // 努力工作十天
  tLens // 努力工作十天

  wLens// 复用之前的单反,只更换镜头
  // 拍照

  tLens// 拍照

  // 是不是感觉棒棒嗒
  //: capture with normal lens
  //: capture with wide-angle lens
  //: capture with normal lens
}

测试框架猜测

比如Android的测试框架通常会出现形如@Inject的注释,它都做了些什么事情呢?下面给出一个完整可运行的例子程序来说明一下:

string // @Inject // <--你在这里说明了L是需要注入的,通常用注释来说明注入 L "telephoto lens" //* fmt.Println("capture with",m[c.L.LensType]) // 这是你在程序中的代码,框架看到你使用了c.L // 默默的给你做了如下替换: getLensLensType} // 由框架在编译时插入的方法 Lens l Lens l } } l }} // 你需要用某种方式进行注入 () }

好了,以上解释了依赖注入的使用和原理,如果你要读懂或者写一个框架的话,反射和依赖注入都是很有用的特征,那就掌握它吧。或者用的时候来读读这篇文章吧。

上述注入器的bug

你一定发现了上述注入器的bug:

"daniu""opensse"c "Hello,") // 本来想给三个人say hello,结果发现给最后一个人说了三次 //: Hello,opensse opensse opensse })

你会发现单用类型做拓扑的注入器,只会持有最新一次类型的值。那么怎么办呢?没有解决不了的问题,传个[]string也是极好的。如果参数的结构比较固定的话,封装成struct也是不错的选择,Go允许给struct打Tag,而reflect也支持对Tag的访问,如果对这部分感兴趣,推荐你去读一下"encoding/xml","encoding/json"包的源码。

什么? 还有bug!!!,留言或者发邮件告诉我吧,欢迎各种骚扰。另外,赞赏作者里有微信号哦,一般人我不告诉他。

理解依赖注入了吗?现在读odegangsta/inject是不是感觉很溜呀?facebook还开源了一套基于有向图实现的注入器facebookgo/inject感觉屌屌的。如果对Java情有独钟,那就研究一下Dagger吧,相信你一定会有所收获。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结