当第一次遇到控制反转(IoC)时,它可能非常令人困惑。

这是怎么一回事?它解决了哪个问题?什么时候使用合适,什么时候不合适?


当前回答

假设我们在酒店开会。

我们邀请了很多人,所以我们漏掉了很多壶水和很多塑料杯。

当有人想喝水时,他/她将杯子装满,喝水,然后将杯子扔在地板上。

大约一个小时后,我们的地板上覆盖着塑料杯和水。

让我们在反转控件后尝试:

想象一下,在同一地点举行同一次会议,但我们现在有一个服务员只带一个玻璃杯,而不是塑料杯(Singleton)

当有人想喝酒时,服务员会给他们一杯。他们把它喝了,然后还给服务员。

抛开卫生问题不谈,使用服务员(过程控制)更有效、更经济。

这正是Spring(另一个IoC容器,例如:Guice)所做的。Spring IoC没有让应用程序使用新的关键字(例如,拿一个塑料杯子)创建所需的东西,而是为应用程序提供所需对象(一杯水)的同一杯子/实例(singleton)。

把自己想象成这样一个会议的组织者:

示例:-

public class MeetingMember {

    private GlassOfWater glassOfWater;

    ...

    public void setGlassOfWater(GlassOfWater glassOfWater){
        this.glassOfWater = glassOfWater;
    }
    //your glassOfWater object initialized and ready to use...
    //spring IoC  called setGlassOfWater method itself in order to
    //offer to meetingMember glassOfWater instance

}

有用的链接:-

http://adfjsf.blogspot.in/2008/05/inversion-of-control.htmlhttp://martinfowler.com/articles/injection.htmlhttp://www.shawn-barrett.com/blog/post/Tip-of-the-day-e28093-Inversion-Of-Control.aspx

其他回答

维基百科文章。对我来说,控制反转就是将您按顺序编写的代码转换为委托结构。您的程序不是显式地控制一切,而是设置一个类或库,其中包含发生某些事情时要调用的某些函数。它解决了代码重复。例如,在过去,您可以手动编写自己的事件循环,在系统库中轮询新事件。现在,大多数现代API只需告诉系统库您感兴趣的事件,它会让您知道它们何时发生。控制反转是减少代码重复的一种实用方法,如果您发现自己复制了整个方法,只更改了一小段代码,可以考虑使用控制反转来解决它。在许多语言中,通过委托、接口甚至原始函数指针的概念,控制反转变得容易。它并不适合在所有情况下使用,因为这样编写时,程序的流程可能更难遵循。在编写可重用的库时,这是一种设计方法的有用方法,但除非它真的解决了代码重复问题,否则应该在自己程序的核心中谨慎使用。

我同意NilObject,但我想补充一点:

如果你发现自己复制了一个完整的方法,只修改了一小段代码,你可以考虑用控制反转来处理它

如果你发现自己到处复制和粘贴代码,你几乎总是在做错事。编码为“一次且仅一次”的设计原则。

在使用“控制反转”之前,你应该充分了解它的优点和缺点,如果你这样做,你应该知道为什么要使用它。

赞成的意见:

您的代码被解耦,因此您可以轻松地将接口的实现与其他实现交换它是针对接口而非实现进行编码的强大动力为代码编写单元测试是非常容易的,因为它只依赖于它在构造函数/setter中接受的对象,并且可以很容易地用正确的对象单独初始化它们。

欺骗:

IoC不仅会反转程序中的控制流,还会使其变得相当模糊。这意味着你不能再只读取代码并从一个地方跳到另一个地方,因为代码中通常存在的连接不再存在。相反,它在XML配置文件或注释中以及IoC容器的代码中解释这些元数据。出现了一类新的错误,即您的XML配置或注释错误,您可以花费大量时间来找出IoC容器在特定条件下向其中一个对象注入空引用的原因。

就我个人而言,我看到了IoC的优点,我非常喜欢它们,但我倾向于尽可能避免使用IoC,因为它将您的软件变成一个类的集合,这些类不再构成“真正的”程序,而只是需要通过XML配置或注释元数据组合在一起的东西,如果没有IoC,它就会崩溃。

我将写下我对这两个术语的简单理解:

For quick understanding just read examples*

依赖注入(DI):依赖注入通常意味着将方法依赖的对象作为参数传递给方法,而不是让方法创建依赖对象。这在实践中意味着,该方法不直接依赖于特定的实现;任何满足要求的实现都可以作为参数传递。使用此对象可以告诉它们的依赖关系。春天使它成为可能。这导致了松散耦合的应用程序开发。

Quick Example:EMPLOYEE OBJECT WHEN CREATED,
              IT WILL AUTOMATICALLY CREATE ADDRESS OBJECT
   (if address is defines as dependency by Employee object)

控制反转(IoC)容器:这是框架的共同特征,IOC通过其BeanFactory管理java对象——从实例化到销毁-由IoC容器实例化的Java组件称为bean,IoC容器管理bean的范围、生命周期事件以及为其配置和编码的任何AOP特性。

快速示例:控制反转是指获得自由、更大的灵活性和更少的依赖性。当你使用台式电脑时,你是从属的(或者说,受控的)。你必须坐在屏幕前看着屏幕。用键盘打字,用鼠标导航。一个糟糕的书面软件会让你更加痛苦。如果你用笔记本电脑取代了你的桌面,那么你的控制就有点颠倒了。你可以轻松地拿着它四处走动。所以现在你可以用电脑控制你的位置,而不是电脑控制它。

通过实现控制反转,软件/对象消费者可以获得更多的软件/对象控制/选项,而不是被控制或拥有更少的选项。

作为设计指南的控制反转具有以下目的:

某个任务的执行与实现是分离的。每个模块都可以专注于它的设计目的。模块不假设其他系统做什么,而是依赖它们的合同。替换模块对其他模块没有任何副作用,我将在这里保持抽象,您可以访问以下链接以详细了解主题。一个很好的例子

详细说明

我在这里找到了一个非常清楚的例子,它解释了“控制是如何颠倒的”。

经典代码(无依赖注入)

以下是不使用DI的代码大致工作原理:

应用程序需要Foo(例如控制器),因此:应用程序创建Foo应用程序调用FooFoo需要Bar(例如服务),因此:Foo创建BarFoo调用BarBar需要Bim(服务、存储库…),因此:条形图创建Bim酒吧有点事

使用依赖注入

以下是使用DI的代码大致工作原理:

应用程序需要Foo,需要Bar,需要Bim,因此:应用程序创建Bim应用程序创建Bar并赋予它Bim应用程序创建Foo并给它Bar应用程序调用FooFoo调用Bar酒吧有点事

依赖项的控制是从一个被调用到另一个调用的。

它解决了什么问题?

依赖注入使得可以很容易地与注入类的不同实现进行交换。在单元测试时,您可以注入一个虚拟实现,这使测试更加容易。

例如:假设您的应用程序将用户上传的文件存储在Google Drive中,使用DI,您的控制器代码可能如下所示:

class SomeController
{
    private $storage;

    function __construct(StorageServiceInterface $storage)
    {
        $this->storage = $storage;
    }

    public function myFunction () 
    {
        return $this->storage->getFile($fileName);
    }
}

class GoogleDriveService implements StorageServiceInterface
{
    public function authenticate($user) {}
    public function putFile($file) {}
    public function getFile($file) {}
}

当你的需求发生变化时,比如说,你被要求使用Dropbox而不是GoogleDrive。您只需要为StorageServiceInterface编写一个dropbox实现。只要Dropbox实现符合StorageServiceInterface,就不必对控制器进行任何更改。

测试时,您可以使用虚拟实现为StorageServiceInterface创建模拟,其中所有方法都返回null(或根据测试要求的任何预定义值)。

相反,如果您有一个控制器类来构造具有如下新关键字的存储对象:

class SomeController
{
    private $storage;

    function __construct()
    {
        $this->storage = new GoogleDriveService();
    }

    public function myFunction () 
    {
        return $this->storage->getFile($fileName);
    }
}

当您想要使用Dropbox实现进行更改时,必须替换构建新GoogleDriveService对象的所有行,并使用DropboxService。此外,在测试SomeController类时,构造函数总是期望GoogleDriveService类,并触发该类的实际方法。

什么时候合适,什么时候不合适?在我看来,当您认为类有(或可能有)替代实现时,您可以使用DI。