关于OOP:什么是控制反转?

What is Inversion of Control?

当第一次遇到控制反转(或IOC)时,它会非常混乱。

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

  • 控制反转(IOC)和依赖注入(DI)模式都是关于从代码中删除依赖关系。

    例如,假设您的应用程序有一个文本编辑器组件,并且您希望提供拼写检查。您的标准代码如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    public class TextEditor {

        private SpellChecker checker;

        public TextEditor() {
            this.checker = new SpellChecker();
        }
    }

    我们在这里所做的创建了TextEditorSpellChecker之间的依赖关系。在国际奥委会的设想中,我们会这样做:

    1
    2
    3
    4
    5
    6
    7
    8
    public class TextEditor {

        private IocSpellChecker checker;

        public TextEditor(IocSpellChecker checker) {
            this.checker = checker;
        }
    }

    在第一个代码示例中,我们正在实例化SpellChecker(this.checker = new SpellChecker();),这意味着TextEditor类直接依赖于SpellChecker类。

    在第二个代码示例中,我们通过在TextEditor构造函数签名中使用SpellChecker依赖类(而不是在类中初始化依赖项)来创建抽象。这允许我们调用依赖项,然后将其传递给textEditor类,如下所示:

    1
    2
    SpellChecker sc = new SpellChecker; // dependency
    TextEditor textEditor = new TextEditor(sc);

    现在,创建TextEditor类的客户机拥有使用SpellChecker实现的控制权,因为我们正在向TextEditor签名注入依赖项。

    这只是一个简单的例子,SimoneBusoli有一系列很好的文章对其进行了更详细的解释。


    控制反转是当程序回调时得到的,例如,像一个GUI程序。

    例如,在旧的学校菜单中,您可能有:

    1
    2
    3
    4
    5
    6
    print"enter your name"
    read name
    print"enter your address"
    read address
    etc...
    store in database

    从而控制用户交互的流程。

    在GUI程序或其他类似程序中,我们会说:

    1
    2
    3
    when the user types in field a, store it in NAME
    when the user types in field b, store it in ADDRESS
    when the user clicks the save button, call StoreInDatabase

    现在控制权颠倒了…与计算机接受用户按固定顺序输入不同,用户控制输入数据的顺序以及数据保存在数据库中的时间。

    基本上,任何具有事件循环、回调或执行触发器的事件都属于此类。


    什么是控制反转?

    如果您遵循这两个简单的步骤,您已经完成了控制反转:

  • 将要做的部分与要做的部分分开。
  • 确保当部分尽可能不了解什么部分时;反之亦然。
  • 基于您在实现中使用的技术/语言,这些步骤中的每一步都有几种可能的技术。

    ——

    控制反转(IOC)的反转部分是一件令人困惑的事情,因为反转是相对术语。理解国际奥委会的最好方法就是忘记这个词!

    ——

    实例

    • 事件处理。事件处理程序(要做的部分)——引发事件(何时做部分)
    • 接口。组件客户端(何时做部件)--组件接口实现(什么时候做部件)
    • XUnn-固定物设置和拆卸(要做的部分)——XUnit框架在开始时调用设置,在结束时调用拆卸(要做的部分)
    • 模板方法设计模式。模板方法何时做部件--基元子类实现什么做部件
    • COM中的dll容器方法。dllmain、dllcanuload等(要做的部分)——COM/OS(要做的部分时间)


    控制反转是关于分离关注点。

    没有IOC:你有一台笔记本电脑,你不小心打破了屏幕。达恩,你会发现同样型号的笔记本电脑屏幕根本不在市场上。所以你被卡住了。

    与国际奥委会:你有一台台式电脑,你不小心打破了屏幕。你会发现你可以从市场上买到几乎所有的桌面显示器,它与你的桌面很好地配合。

    在这种情况下,您的桌面成功地实现了IOC。它接受各种类型的显示器,而笔记本电脑不接受,它需要一个特定的屏幕来得到修复。


    控制权的倒置(或IOC)是关于获得自由(你结婚了,你失去了自由,你被控制了)。你离婚了,你刚刚实现了控制反转。这就是我们所说的"脱钩"。良好的计算机系统会阻碍一些亲密的关系。)更大的灵活性(办公室的厨房只提供干净的自来水,这是你想喝的唯一选择。你的老板通过安装一台新的咖啡机实现了控制权的倒置。现在你有了选择自来水或咖啡的灵活性)和更少的依赖性(你的伴侣有工作,你没有工作,你在经济上依赖你的伴侣,所以你是被控制的。你找到了一个工作,你实现了控制反转。良好的计算机系统鼓励依赖。)

    当你使用台式电脑的时候,你已经努力了(或者说,控制了)。你必须坐在屏幕前看它。使用键盘键入并使用鼠标导航。一个写得不好的软件会让你更加苦恼。如果你用笔记本电脑替换你的桌面,那么你就有点反向控制了。你可以很容易地拿着它四处走动。所以现在你可以控制你的电脑在哪里,而不是你的电脑控制它。

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

    考虑到上述想法。我们仍然怀念国际奥委会的一个重要组成部分。在IOC的场景中,软件/对象消费者是一个复杂的框架。这意味着您创建的代码不是由您自己调用的。现在让我们解释一下为什么这种方法对Web应用程序更有效。

    假设您的代码是一组工人。他们需要造一辆车。这些工人需要一个地方和工具(软件框架)来建造汽车。传统的软件框架就像一个拥有许多工具的车库。因此,工人们需要自己制定计划,并使用工具来制造汽车。造一辆车不是一件容易的事情,工人们很难正确地计划和合作。一个现代化的软件框架将像一个现代化的汽车工厂,所有的设施和管理人员都到位了。工人不必制定任何计划,管理者(框架的一部分,他们是最聪明的人,制定了最复杂的计划)将帮助协调,以便工人知道何时做他们的工作(框架调用您的代码)。工人只需要足够灵活地使用管理者提供给他们的任何工具(通过使用依赖注入)。

    尽管工人们把管理项目的最高级别的控制权交给管理者(框架)。但有专业人士帮忙是件好事。这是国际奥委会的理念真正来源。

    具有MVC体系结构的现代Web应用程序依赖于框架来执行URL路由,并将控制器放置到位,以便框架调用。

    依赖注入和控制反转相关。依赖注入处于微观层面,控制反转处于宏观层面。为了完成一顿饭,你必须吃每一口(实现DI)(实现IOC)。


    在使用控制反转之前,您应该很清楚它有其优点和缺点,如果这样做,您应该知道为什么要使用它。

    赞成的意见:

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

    欺骗:

    • IOC不仅颠倒了程序中的控制流,而且使它变得非常模糊。这意味着您不能再仅仅读取代码并从一个地方跳到另一个地方,因为代码中通常存在的连接已经不在代码中了。相反,它位于XML配置文件或注释中,以及解释这些元数据的IOC容器的代码中。
    • 出现了一类新的bug,在这种bug中,您的XML配置或注释出现错误,并且您可以花费大量时间来查明为什么IOC容器在某些条件下向某个对象注入空引用。

    我个人认为IOC的优点,我非常喜欢它们,但我倾向于尽可能避免IOC,因为它将您的软件变成一组类,这些类不再构成"真实"程序,而是需要通过XML配置或注释元数据组合在一起,如果没有这些类,它们就会分崩离析。


  • 维基百科文章。对我来说,控制反转正在将顺序编写的代码转换为委托结构。程序不是显式地控制所有东西,而是设置一个类或库,在某些事情发生时调用某些函数。

  • 它解决了代码复制问题。例如,在过去,您将手动编写自己的事件循环,在系统库中轮询新事件。现在,大多数现代的API您只需告诉系统库您感兴趣的事件,当它们发生时,它就会让您知道。

  • 控制反转是减少代码重复的一种实用方法,如果您发现自己复制了整个方法,并且只更改了一小段代码,则可以考虑使用控制反转来处理它。在许多语言中,通过委托、接口甚至原始函数指针的概念,可以很容易地实现控制反转。

    在所有情况下都不适合使用,因为这样编写程序时很难遵循程序流。在编写将被重用的库时,它是设计方法的一种有用方法,但除非它真正解决了代码复制问题,否则应该在您自己的程序的核心中谨慎地使用它。


  • 但我认为你必须非常小心。如果您将过度使用这个模式,您将进行非常复杂的设计,甚至更复杂的代码。

    就像这个例子中的texteditor一样:如果您只有一个拼写检查程序,那么可能不需要使用ioc?除非你需要写单元测试之类的东西…

    不管怎样:讲道理。设计模式是很好的实践,但不是要宣扬的圣经。不要到处粘。


    假设你是一个物体。你去一家餐馆:

    没有IOC:你要求"苹果",当你要求更多的时候,你总是得到苹果的服务。

    与国际奥委会:你可以要求"水果"。每次上菜你都能得到不同的水果。例如,苹果、橘子或西瓜。

    所以,很明显,当你喜欢这些品种时,IOC更受欢迎。


    IOC/DI对我来说是在推动对调用对象的依赖性。超级简单。

    非技术性的答案是,在你打开汽车之前就可以换掉它的引擎。如果所有的东西都连接正确(接口),那么您就是好的。


  • 控制反转是用于将系统中的组件和层分离的模式。该模式是通过在构造组件时将依赖项注入组件来实现的。这些依赖项通常作为接口提供,用于进一步解耦和支持可测试性。IOC/DI容器(如温莎城堡、Unity)是可用于提供IOC的工具(库)。这些工具提供了超越简单依赖性管理的扩展特性,包括生存期、AOP/拦截、策略等。

  • A.减轻了组件管理其依赖性的责任。B.提供在不同环境中交换依赖实现的能力。C.允许通过模拟依赖项来测试组件。D.提供了一种在整个应用程序中共享资源的机制。

  • A.进行测试驱动开发时至关重要。如果没有IOC,则很难进行测试,因为测试中的组件与系统的其余部分高度耦合。B.开发模块化系统时至关重要。模块化系统是一种系统,其组件可以在不需要重新编译的情况下进行更换。c.如果有许多跨领域的问题需要解决,尤其是在企业应用程序中,这一点至关重要。


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

    1
    For quick understanding just read examples*

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

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

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

    QUICK EXAMPLE:Inversion of Control is about getting freedom, more flexibility, and less dependency. When you are using a desktop computer, you are slaved (or say, controlled). You have to sit before a screen and look at it. Using keyboard to type and using mouse to navigate. And a bad written software can slave you even more. If you replaced your desktop with a laptop, then you somewhat inverted control. You can easily take it and move around. So now you can control where you are with your computer, instead of computer controlling it

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

    作为设计指南的控制反转可用于以下目的:

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

    详细说明


    只回答第一部分。这是怎么一回事?

    控制反转(IOC)意味着首先创建依赖项实例,然后创建类的后一个实例(可以选择通过构造函数注入这些实例),而不是先创建类的实例,然后创建依赖项的实例。因此,控制反转反转程序的控制流。与被调用方控制控制控制流(创建依赖项时)不同,调用方控制程序的控制流。


    例如,任务1是创建对象。如果没有IOC的概念,任务1应该由程序员来完成,但是对于IOC的概念,任务1应该由容器来完成。

    简言之,控件从程序员转换为容器。所以,它被称为控制反转。

    我在这里找到了一个很好的例子。


    假设我们在某家旅馆开会。

    很多人,很多克拉的水,很多塑料杯。

    当有人想喝的时候,她会把杯子装满,然后把杯子扔到地上。

    过了一个小时左右,我们的地板上覆盖着塑料杯和水。

    让反转控制。

    同一个会议在同一个地方举行,但我们没有用塑料杯,而是用一个玻璃杯(单人杯)招待一位服务员。

    她总是向客人提供喝酒的机会。

    当有人想喝的时候,她从服务员的杯子里拿出来喝,然后把它还给服务员。

    撇开卫生问题不谈,最后一种形式的饮酒过程控制更为有效和经济。

    这正是Spring(另一个IOC容器,例如guice)所做的。SpringIOC容器不是让应用程序使用新的关键字(拿着塑料杯)来创建它需要的东西,而是一直提供给应用所需对象(一杯水)的相同实例(单例)。

    把你自己当作这次会议的组织者。你需要向酒店管理部门传达这样的信息

    会议成员需要一杯水,而不是一块蛋糕。

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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.html
    • http://martinfowler.com/articles/injection.html
    • http://www.shawn-barrett.com/blog/post/tip-of-the-day-e28093-inversion-of-control.aspx


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

    if you find yourself copying an entire method and only changing a small piece of the code, you can consider tackling it with inversion of control

    如果你发现自己在复制和粘贴代码,你几乎总是在做一些错误的事情。编纂为设计原则一次又一次。


    "IOC"这个缩写词和它所代表的名字最让人困惑的是它太迷人了——几乎是一个杂音名字。

    我们真的需要一个名称来描述过程性编程和事件驱动编程之间的区别吗?好吧,如果我们需要的话,但是我们需要选择一个全新的"比生活更大"的名字吗?这个名字比它解决的问题还要复杂。


    这里有一个非常简单的书面解释

    http://binstock.blogspot.in/2008/01/excellent-explanation-of-dependency.html

    它说——

    "Any nontrivial application is made up of two or more classes that
    collaborate with each other to perform some business logic.
    Traditionally, each object is responsible for obtaining its own
    references to the objects it collaborates with (its dependencies).
    When applying DI, the objects are given their dependencies at creation
    time by some external entity that coordinates each object in the
    system. In other words, dependencies are injected into objects."


    IOC是关于颠倒代码和第三方代码(库/框架)之间的关系:

    • 在正常的S/W开发中,编写main()方法并调用"library"方法。您可以控制:)
    • 在IOC中,"框架"控制main()并调用您的方法。框架处于控制状态:(

    DI(依赖注入)是关于控制如何在应用程序中流动的。传统的桌面应用程序有从应用程序(main()方法)到其他库方法调用的控制流,但是使用DI控制流是反向的,框架负责启动应用程序、初始化应用程序以及在需要时调用方法。

    最后你总是赢的:)


    控制权的倒置是当你去杂货店,而你的妻子给你买的产品清单。

    在编程方面,她将回调函数getProductList()传递给正在执行的函数doShopping()

    它允许函数的用户定义函数的某些部分,使其更加灵活。


    控制反转是一个通用原则,而依赖注入将此原则实现为对象图构造的设计模式(即配置控制对象彼此引用的方式,而不是对象本身控制如何获取对另一个对象的引用)。

    把控制反转看作一种设计模式,我们需要看看我们在反转什么。依赖注入反转了构造对象图的控制。如果用外行的术语来说,控制反转意味着程序中控制流的改变。例如,在传统的独立应用程序中,我们有一个主要的方法,从这里控制权被传递到其他第三方库(例如,我们使用了第三方库的功能),但是通过控制权的反转,控制权从第三方库代码转移到我们的代码,因为我们正在接受第三方库的服务。但是还有一些其他方面需要在程序中反转,例如调用方法和线程来执行代码。

    对于那些对控制反转更深入的人,已经发表了一篇论文,概述了控制反转作为一种设计模式的更完整图片(OfficeFloor:使用Office模式改进软件设计http://doi.acm.org/10.1145/2739011.2739013,可从http://www.officeFloor.net/about.ht下载免费副本ml)。

    确定的关系如下:

    控制反转(用于方法)=dependency(state)injection+continue injection+thread injection

    上述控制反转关系总结-http://dzone.com/articles/inversion-of-coupling-control


    我在这里找到了一个非常清楚的例子,解释了"控件是如何反转的"。

    经典代码(无依赖项注入)

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

    • 应用程序需要foo(例如控制器),因此:
    • 应用程序创建foo
    • 应用程序调用foo
      • FOO需要酒吧(例如服务),因此:
      • FO创建条
      • FO调用条
        • BAR需要BIM(服务、存储库等),因此:
        • BAR创建BIM
        • 酒吧做点什么

    使用依赖注入

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

    • 应用需要foo,需要bar,需要bim,所以:
    • 应用程序创建BIM
    • 应用程序创建BAR并给出BIM
    • 应用程序创建foo并赋予它条
    • 应用程序调用foo
      • FO调用条
        • 酒吧做点什么

    依赖项的控制从一个被调用转换为一个调用。

    它能解决什么问题?

    依赖注入使得与注入类的不同实现交换变得容易。虽然单元测试可以注入一个虚拟实现,这使得测试更加容易。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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创建模拟,其中所有方法都返回空值(或根据测试要求的任何预定义值)。

    相反,如果您让Controller类使用这样的new关键字构造存储对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class SomeController
    {
        private $storage;

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

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

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

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


    程序设计口语

    IOC简单地说:它使用接口作为特定事物(如字段或参数)的一种方式,作为一些类可以使用的通配符。它允许代码的可重用性。

    例如,假设我们有两个类:狗和猫。两者具有相同的品质/状态:年龄、尺寸、重量。因此,我不需要创建一个名为dog service和cat service的服务类,而是创建一个名为animalservice的服务类,它只允许在使用接口ianimal时使用dog和cat。

    然而,从实用主义的角度来说,它有一些倒退。

    a)大多数开发人员不知道如何使用它。例如,我可以创建一个名为customer的类,并且可以自动(使用IDE的工具)创建一个名为i customer的接口。因此,找到一个包含类和接口的文件夹并不罕见,不管这些接口是否会被重用。这叫浮肿。有些人可能会争辩说"将来我们可能会用到它"。- -

    b)有一些限制。例如,让我们讨论一下狗和猫的情况,我想为狗添加一个新的服务(功能)。假设我要计算训练狗的天数(trainDays()),因为猫没用,猫不能训练(我开玩笑)。

    B.1)如果我将trainDays()添加到服务animalservice中,那么它也可以与cats一起使用,而且根本无效。

    B.2)我可以在trainDays()中添加一个条件,在该条件中它评估使用的是哪个类。但这将完全打破国际奥委会。

    B.3)我可以为新功能创建一个名为DogService的新服务类。但是,它将提高代码的可维护性,因为我们将为Dog提供两类服务(具有类似的功能),而且这很糟糕。


    我喜欢这样的解释:http://joelabrahamsson.com/inversion-of-control-an-introduction-with-examples-in-net/

    它开始时很简单,也显示了代码示例。

    enter image description here

    消费者x需要消耗的类y来完成某些事情。这一切都很好,很自然,但是x真的需要知道它使用y吗?

    X知道它使用的是Y的行为、方法、属性等,而不知道是谁真正实现了这个行为,这还不够吗?

    通过提取x在y中使用的行为的抽象定义(如下面的i所示),并让消费者x使用该行为的实例而不是y,它可以继续执行它所做的操作,而不必知道y的具体情况。

    enter image description here

    在上面的插图中,Y实现了I,X使用了I的一个实例。虽然X仍然可能使用Y,但有趣的是X不知道这一点。它只知道它使用了实现i的东西。

    阅读文章了解更多信息和福利描述,如:

    • x不再依赖y了
    • 更灵活的是,可以在运行时确定实现
    • 隔离代码单元,更容易测试


    我知道这里已经给出了答案。但我仍然认为,关于控制反转的一些基础知识必须在这里详细讨论,以供将来的读者使用。

    控制反转(IOC)建立在一个非常简单的原理上,叫做好莱坞原理。上面说,

    Don't call us, we'll call you

    它的意思是,不要去好莱坞实现你的梦想,如果你是值得的,那么好莱坞会找到你,让你的梦想成真。相当倒转,嗯?

    现在,当我们讨论国际奥委会的原则时,我们常常忘记好莱坞。对于国际奥委会来说,必须有三个要素,一个好莱坞,你和一个任务来实现你的梦想。

    在我们的编程世界中,好莱坞代表一个通用框架(可能由您或其他人编写),您代表您编写的用户代码,任务代表您希望用代码完成的事情。现在你再也不会自己去触发你的任务,而不是在国际奥委会!相反,您设计的一切都是这样的:您的框架将为您触发您的任务。因此,您已经构建了一个可重用的框架,它可以使某人成为英雄,或使另一个人成为坏人。但这个框架始终是负责的,它知道何时选择某人,而某人只知道它想成为什么样的人。

    这里将给出一个真实的例子。假设您想要开发一个Web应用程序。因此,您可以创建一个框架来处理Web应用程序应该处理的所有常见问题,如处理HTTP请求、创建应用程序菜单、服务页面、管理cookie、触发事件等。

    然后在您的框架中留下一些钩子,您可以在其中放置更多的代码来生成自定义菜单、页面、cookie或记录一些用户事件等。对于每个浏览器请求,您的框架将运行并执行您的自定义代码(如果钩子),然后将其返回到浏览器。

    所以,这个想法非常简单。与其创建一个可以控制一切的用户应用程序,不如先创建一个可以控制一切的可重用框架,然后编写自定义代码并将其挂接到框架上,以便及时执行这些代码。

    Laravel和EJB就是这样一个框架的例子。

    参考文献:

    https://martinfowler.com/bliki/inversionofcontrol.html

    https://en.wikipedia.org/wiki/inversionu控制


    控制反转是将控制从库传输到客户机。当我们谈论将函数值(lambda表达式)注入(传递)到控制(更改)库函数行为的高阶函数(库函数)中的客户机时,这就更有意义了。向库中注入库依赖项(承载行为)的客户机或框架也可以被视为IOC。


    使用IOC并不是新的。您的IOC容器将这样做并管理它们的生命周期。

    它解决了必须手动将一个对象类型的每个实例化更改为另一个对象类型的问题。

    当您的功能将来可能会改变,或者根据中使用的环境或配置不同而有所不同时,这是适当的。


  • 所以上面的数字1。什么是控制反转?

  • 维护是它为我解决的第一件事。它保证我使用的是接口,这样两个类就不会彼此密切联系。

  • 在使用像城堡温莎这样的容器时,它可以更好地解决维护问题。在不更改代码行的情况下,能够将数据库中的组件换成使用基于文件的持久性的组件是非常棒的(配置更改完成了)。

    一旦你了解了仿制药,它会变得更好。想象一下,有一个消息发布者接收记录并发布消息。它不关心它发布了什么,但它需要一个映射器来从一个记录到一条消息。

    1
    2
    3
    4
    5
    6
    7
    public class MessagePublisher<RECORD,MESSAGE>
    {
        public MessagePublisher(IMapper<RECORD,MESSAGE> mapper,IRemoteEndpoint endPointToSendTo)
        {
          //setup
        }
    }

    我曾经写过一次,但是现在如果我发布不同类型的消息,我可以在这组代码中注入许多类型。我还可以编写映射器,它获取相同类型的记录并将它们映射到不同的消息。在泛型中使用DI使我能够编写很少的代码来完成许多任务。

    哦,是的,存在可测试性问题,但它们是IOC/DI的第二大优势。

    我非常喜欢IOC/DI。

    三。当您有一个稍微复杂一些的中型项目时,它就变得更加合适了。当你开始感到疼痛的时候,我会说这是合适的。


    为了理解这个概念,控制反转(IOC)或依赖反转原理(DIP)涉及两个活动:抽象和反转。依赖注入(DI)只是少数几种反演方法之一。

    要了解更多信息,您可以在此处阅读我的博客

  • 这是怎么一回事?
  • 这是一种让实际行为来自边界之外的实践(面向对象编程中的类)。边界实体只知道它的抽象(例如接口、抽象类、面向对象编程中的委托)。

  • 它能解决什么问题?
  • 在编程方面,IOC试图通过使其模块化、分离其各个部分并使其单元可测试来解决单片代码。

  • 什么时候合适,什么时候不合适?
  • 大多数情况下,这是合适的,除非您有只需要单片代码的情况(例如非常简单的程序)


    在类内创建一个对象称为紧耦合,Spring通过遵循设计模式(DI/IOC)消除了这种依赖性。其中,类的对象传入构造函数而不是在类中创建。此外,我们在构造函数中给出了超类引用变量来定义更一般的结构。


    为了理解IOC,我们应该讨论依赖倒置。

    依赖倒置:依赖抽象,而不是具体化。

    控制反转:主与抽象,以及主是如何粘合系统的。

    DIP and IoC

    以下是一些关于这方面的好文章:

    https://coderstower.com/2019/03/26/dependency-inversion-why-you-shouldnt-avolve-it/

    https://coderstower.com/2019/04/02/main-and-abstraction-the-discoupled-peers/

    Inversion of Control: Putting All Together


    控制反转意味着控制组件(类)的行为。为什么它被称为"反转",因为在这个模式之前,类是硬连接的,并且对于它们将要做什么是确定的,例如。

    导入一个具有TextEditorSpellChecker类的库。现在,这个SpellChecker只会检查英语的拼写。假设您希望TextEditor能够处理德语,并且能够拼写检查,那么您可以控制它。

    在国际奥委会的情况下,这种控制是颠倒的,也就是说,它是如何给你的?库将实现如下功能:

    它将有一个TextEditor类,然后它将有一个ISpeallChecker类(它是一个接口而不是concret SpellChecker类),当您在ioc容器(例如spring)中配置东西时,您可以提供自己的"ispellchecker"实现,它将检查德语的拼写。所以拼写检查的工作方式的控制是不垂直的,是从那个库中取出来的,给你的。这就是国际奥委会。


    它是什么?反转(耦合)控制,改变耦合方向为方法签名。对于反向控制,方法签名的定义由方法实现(而不是方法的调用方)决定。这里有完整的解释

    它能解决什么问题?自上而下的耦合方法。这随后消除了重构的需要。

    什么时候使用合适,什么时候不合适?对于定义良好、不受太大变化影响的小型应用程序,这可能是一种开销。然而,对于将要发展的定义较少的应用程序,它减少了方法签名的固有耦合。这给了开发人员更多的自由来开发应用程序,避免了对代码进行昂贵的重构。基本上,允许应用程序在很少的返工的情况下发展。