控制反转与好莱坞原则的混淆

Posted

技术标签:

【中文标题】控制反转与好莱坞原则的混淆【英文标题】:Confusion between Inversion of Control and Hollywood Principle 【发布时间】:2017-10-02 19:55:35 【问题描述】:

我正在阅读 Head First Design 模式,只是坚持好莱坞原则。早些时候我读到了控制反转,我的理解是,它是一种设计原则(有些人也称之为模式),通过它传统的程序流程从“高级模块调用低级模块”变为“ 低级模块调用高级模块”(一般通过抽象),所以我们可以对特定的低级模块有非常低的依赖,改变低级模块不会对我们的高级或高级模块产生任何影响靠近业务模块。

但是当作者说出以下有关好莱坞原则的内容时,我感到困惑:-

在第 296 页

根据好莱坞原则,我们允许低级组件 将自己挂钩到一个系统中,但高层 组件决定何时需要以及如何需要。在 换句话说,高级组件给了低级 组成“不要打电话给我们,我们会打电话给你”的待遇。

最后一行是说高层组件给低层 组件“不要打电话给我们,我们会打电话给你”处理。这意味着我们的高级组件实际上正在调用低级组件,因此 这似乎打破了控制倒置原则和依赖倒置原则。

请澄清一下。

【问题讨论】:

你认为为什么首先在这里使用控制反转?您可以说的唯一方法是允许低级组件 (llc) 向 hlc 注册。除此之外,所有活动(即完全控制)都由 hlc 保留。 @TaW 您将控制反转限制为基于事件的编程(观察者模式),但我们也可以通过依赖注入来实现它。 @Taw 当您编写应用程序并使用 Windows 框架通过 UI 获取事件时,根据您的评论“允许低级组件 (llc) 向 hlc 注册。”根据您的定义,因为我们的应用程序正在向 windows 框架注册,它是低级组件(llc),而 windows 框架是 hlc,这是完全错误的,应该是相反的顺序(我们的应用程序在这里是 hlc)。 【参考方案1】:

当我们设计软件时,我们会实现 API 和框架这两个方面。

API 发布一些端点,以便调用者使用这些端点获取一些有用的信息,因此调用者没有任何操作点可采取,只有端点和输出。

框架从调用者那里获取策略或业务实现,并在需要时调用它。

在好莱坞原则中,我们可以为我们的策略或业务实现提供反馈,表示框架实现,在需要时调用反馈策略。

控制反转和依赖注入是去除应用程序的依赖。这使得系统更加解耦和可维护。

如果您回到过去的计算机编程时代,程序流过去常常在自己的控制下运行。

如果您仔细分析程序流程,它是顺序的。程序由他自己控制。控制权的反转意味着程序将控制权委托给将驱动流程的其他人。

【讨论】:

【参考方案2】:
class Customer 
   
   public function getCustomers() 
      $conn = new mysqlConnection();
      return $conn->getCustomers();
   
   
   public function iocCustomers(MySQLConnection $conn) 
      return $conn->getCustomers();
   

对于getCustomers(),我们在Customer 类函数中创建Database 对象——深度耦合。每当创建Customer 对象时,也会创建MySQLConnection 对象,这不是必需的。

如果您考虑iocCustomers(),当我们调用函数时,我们也会传递数据库对象。这里Customer 类在需要数据之前不关心数据库,它会在需要时使用它。就像,您无需成为in Hollywood 就有机会,当您需要时,他们会打电话给您,例如,MySQLConnection 对象是 injected 到类 Customer 所需的函数。

这里发生了两件事。

    Customer class 无需担心/知道谁提供数据。换句话说,客户端/调用类Customer 无法控制数据。 [SOLID 原则中的 SRP]

    连接对象获得对数据的控制权,它可以决定它需要从哪个数据库(MySQL 或 SQLite)提供数据。如果我们在getCustomers()中创建对象MySQLConnection,就无法获得切换的灵活性。这称为Inversion of Control。将控制权从调用者转移到生成数据的类。

注意:在第二个功能的情况下,有一些优势。

我们可以将一个名为DBConnectionContract 的接口传递给iocCustomers(),而不是MySQLConnection。这样我们就可以传递任何实现DBConnectionContract 接口的数据库对象(MySQL、Sqlite、Postgres)。这将帮助我们以最少的修改将数据库相互切换。

修改如下:

interface DBConnectionContract 
class MySQLConnection implements DBConnectionContract 
class SQLiteConnection implements DBConnectionContract 
class Customer 
   public function iocCustomers(DBConnectionContract $conn) 

另一个优势与单元测试有关。对于iocCustomers(),我们可以在单元测试时传递模拟数据库对象。因为要对函数进行单元测试,我们需要创建该函数执行所需的环境。

【讨论】:

【参考方案3】:

好莱坞原则与控制反转并不矛盾,尽管它可以通过编程忽略它。

在书中的 Coffee 示例(我猜一页后,我有另一个版本)子类化了两种通过好莱坞原则调用的方法。如果子类只是覆盖了基类中的一些抽象方法,它并没有真正使用控制反转。

但是,由于被调用的子类可以调整基类的成员,因此它可以通过这种方式进行控制。在我之前一页书的问答中,它解释了一个问题:真正应该使用什么钩子。我会尽力解释。

假设你有一个带有未排序列表的基类并调用 print() 列表。如果您有一个可以通过挂钩调用的子类来覆盖 print(),那么该子类可能会决定首先对基类的实际列表进行排序,而不是在需要时调用其他函数。通过这种方式,低级函数可以接管高级函数的控制权。

【讨论】:

因此您将好莱坞原则限制为子类化和覆盖,但事实并非如此,请参见作者还解释的 Array 静态类中的 CompareTo 方法示例。 我在很多地方看到好莱坞原则被称为原则倒置的另一个名称,那又如何呢? 你的第一条评论是对的,这是一个更基本的例子,钩子是通过子类完成的。您还可以将事件或回调对象实现为钩子,并在较低级别的类中实现更多控制。 也许是使用相同的语义,但是对于控制反转,我个人认为更多的是 what 一个类应该负责,而好莱坞原则我更多地考虑如何将控制权交给其他类。虽然维基声明:控制反转有时被戏称为“好莱坞原则:不要打电话给我们,我们会打电话给你”。【参考方案4】:

IoC 有时被称为好莱坞原则。它们是相同的……但是,像所有惯用语一样,它总是受语义的约束。从高层抽象控制低层依赖的原则不是“模式”,而是一个原则。将该原则仅与子分类相关联也不能很好地举例说明,尽管它确实存在。

更常见的是通过组合/聚合来完成,方法是在低级对象中保存对某些接口或抽象类型的引用并将依赖项注入该类。它对接收到的内容没有控制权或责任,它只知道如何处理它。

换句话说,它(低级)必须通过电话等待,而不是要求它进行下一次筛选,试镜?他们在好莱坞怎么称呼它。

【讨论】:

【参考方案5】:

如果我们专注于依赖倒置原则的不同部分,看似矛盾就会得到解决:

A.高级模块不应依赖于低级模块。 两者都应该依赖于抽象。 B. 抽象不应依赖于细节。 细节应该取决于抽象。

这增加了一些上下文,消除了该语句造成的潜在混淆:

高级组件给低级组件“不要打电话给我们,我们会打电话给你”的待遇。

高级组件根本不直接处理低级组件。它们处理代表低级组件的目的或功能的抽象。

所以它不会“破坏”依赖倒置原则。相反,它只需要与该原则相一致地理解。它们是两个不同的原则,所以我们可以应用一个并打破另一个。但是,如果组件之间通过抽象表示,那么我们可以同时应用两者。

他们本可以在有问题的句子中添加说明,但这会使它变得更加冗长和混乱。


FWIW 我经常发现术语“高级”和“低级”令人困惑,因为我们不倾向于使用它们,除非在讨论依赖倒置原则时。无论组件是“高级”还是“低级”,建议都取决于抽象。换句话说,依赖于抽象。我们可以应用该原理,而无需将组件分为高级或低级。

【讨论】:

以上是关于控制反转与好莱坞原则的混淆的主要内容,如果未能解决你的问题,请参考以下文章

控制反转

好莱坞原则:不要调用我,让我来调用你

OCP(开放封闭原则)与 IoC(控制反转)有啥关系?

spring学习总结一----控制反转与依赖注入

控制反转的实现方式

IoC控制反转与DI依赖输入