依赖注入,为什么有很多人说Go语言不需要依赖注入?
依赖注入
依赖注入(Dependency Injection)是一种鼓励模块化的软件工程范式,是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。而通过模块与模块的依赖做解耦,有利于:
模块更加“独立”模块与模块之间“独立”,方便分团队开发模块可以更容易的被其它程序调用,方便构造高度复杂的程序模块更容易被测试上述就是依赖注入的目的,它其实是独立于依赖注入的实现。
更加具体的说,是否使用反射,是否支持在运行时通过修改配置来替换依赖的等等,是属于具体依赖注入实现的特性;它们其实无关于依赖注入范式的考量。
使用依赖注入是为了让程序可以更好模块化管理,将模块与依赖解耦,以便于测试,构造高度复杂的程序;而不是为了在运行时通过修改配置来替换依赖实现。
换句话说,如果我们开发的程序足够简单,那么是没有必要去使用依赖注入范式的;但如果我们开发的是一款高度复杂、需要多团队配合的程序,类似依赖注入这样的范式,则显得必不可少。
Go语言那么go语言是否需要依赖注入?
显然需要,比方说,谷歌开源的go-cloud,这个支持跨云服务商的SDK,便是例子;其团队为了使用依赖注入范式,也顺带开源了wire:一个基于编译时、代码生成实现的依赖注入框架。
Go对独特的语言特性其实不是内置支持goroutine(协程在很多语言其实都有内置或者类库层面的支持),而是其interface接口,在go语言里面,接口的定义可以迟于实现。
开发go程序的时候,我们可以直接编写struct的实现,而无需关心,甚至无法关心具体struct实现了哪些接口。什么样的函数签名组合才构成一个接口,在go语言里面可以是取决于类库、模块的使用者,而不是实现者。
Go语言的接口可以迟于struct实现来定义这一独一无二的语言特性,使得我们在使用依赖注入的时可以先方便的注入具体类型;而在后续需要扩展为“接口”,提供多个实现的时候,无需修改模块代码。
可以说,go语言与依赖注入范式是相当绝妙的搭配。
总结Go语言的interface的特性使得在很多情况下,依赖注入变得没有如Java等语言中重要。然而,依赖注入在一些复杂的场景中也有用武之地,go与依赖注入仍有很大的研究空间。
spring依赖注入有几种方式?
Spring依赖注入有三种主要的方式:
1. 构造函数注入(Constructor Injection):通过构造函数来注入依赖。在类的构造函数中声明依赖的参数,Spring容器会根据参数类型自动注入相应的依赖。
2. Setter方法注入(Setter Injection):通过setter方法来注入依赖。在类中定义相应的setter方法,并在配置文件中通过<property>标签来设置依赖的值。
3. 接口注入(Interface Injection):通过接口来注入依赖。在类中定义一个接口,并在配置文件中通过<property>标签来设置依赖的值。然后,类实现该接口,并在类中实现接口的方法。
除了这三种主要的方式,Spring还提供了其他一些注入方式,如字段注入(Field Injection)和注解注入(Annotation Injection)。字段注入是通过在类的字段上使用@Autowired或@Inject注解来实现的,而注解注入是通过在类或字段上使用自定义的注解来实现的。这些方式都可以根据具体的需求和场景来选择使用。
AngularJS中的依赖注入实际应用场景有哪些?
所谓依赖注入,通俗地举例,有个人养了一只宠物,他可以喂宠物吃东西,宠物会自己吃:
function PetKeeper(pet) {
this.pet = pet;
}
PetKeeper.prototype.feed = function(food) {
this.pet.eat(food);
};
function Pet(type) {
this.type = type;
}
Pet.prototype.eat = function(food) {
alert("I am a " + this.type + ", I'm eating " + food);
};
var tom = new Pet("cat");
var jerry = new Pet("mouse");
var keeper = new PetKeeper(tom);
keeper.feed("fish");
keeper.pet = jerry;
keeper.feed("rice");
这个例子里,pet是外部注入的,在feed函数定义里,并不知道pet到底是什么(在带接口的语言里,至少还是知道是个什么,在动态语言里就是两眼一抹黑了……),只有当它被调用的时候,才知道pet是什么。
这个过程的好处是什么呢?如果我们在PetKeeper内部去创建tom或jerry,就表示PetKeeper要对Pet产生依赖。一个对别人有依赖的东西,它想要单独测试,就需要在依赖项齐备的情况下进行。如果我们在运行时注入,就可以减少这种依赖,比如在单元测试的时候使用模拟类就行。
比如你有一个a,依赖于b,实际业务中,b的实现很复杂:
function A(b) {
this.b = b;
}
A.prototype.a1 = function() {
alert(100 + this.b.b1());
};
function B() {}
B.prototype.b1 = function() {
//这里可能很复杂而且不好模拟,比如依赖于生产环境的一些调用
}
那么,我如何用单元测试来验证A自身的逻辑是正确的呢?如果有强依赖,这里就不好办了,必须实例化真正的B,但是B的调用要依赖于生产环境。换个方式考虑,我们用一个接口与B相同的类来做模拟,只要改变它的返回值,实现各种边界条件,把它的实例注入到A的构造函数中,就可以让A自身的逻辑得到测试了。
function MockB() {}
MockB.prototype.b1 = function() {
return 99;
};
在AngularJS里,依赖注入的目的是为了减少组件间的耦合,它的实现是这个过程:
function Art(Bar, Car) {}
我怎么知道这个Art在实例化的时候要传入Bar和Car的实例呢?形参名称是没法取到的,所以只有狠一点,用toString()来取到刚才这一行字符串,然后用正则表达式取到Bar和Car这两个字符串,然后到模块映射中取到对应的模块,实例化之后传入。
但是这样也有问题,如果这个js被压缩了,很可能命名都变了,压缩成了这样:
function a1(b1, b2) {}
这时候再这样就不知道原先是什么类型了。在这里,有类型声明的语言就不会有问题,比如:
function art(bar:Bar, car:Car) : Art {}
就算你把art, bar, car都改名了,也还是能知道类型,但js里不行。所以,怎么办呢?
aaa.controller("Art", [function(Bar, Car) {}, "Bar", "Car"]);
注意在AngularJS里面,他很可能建议你这么写,但也可以这么写:
Art.$inject = ["Bar", "Car"];
这么一来,我只要拿到Art,就能取到依赖项的名称了,就可以实例化再注入,也不怕压缩了。
angularJS中的API接口和依赖注入该怎么理解呢?
spring是DI的鼻祖 依赖注入设计非常的灵活,使用起来简单方便。感觉angular这些前端框架都在吸收后端的设计思想,像di、组件化、模块化跟后端都是殊途同归。不过现在的angular感觉用起来比较别扭,只能在构造方法中声明注入对象的名称,其描述符和java中的注解虽然长得一样,其实不同的东西。angular还没有用得很深,其他不同待发掘。
控制反转和依赖注入的区别是什么?
区别是侧重点不同: 控制反转的创建对象实例的控制权从代码控制剥离到IOC容器控制,实际就是在xml文件控制,侧重于原理。而依赖注入创建对象时,为这个对象注入属性值或其它对象实例,侧重于实现。