• 控制反转(自译)


        控制反转   

        你遇到可扩展的框架时,你会发现控制反转在这里用得很普遍实际上它经常被用来扩展框架的特征。

        接下来,我们考虑一个简单的例子。假设我正在写一个从用户那里获取信息的程序,并且使用命令行来实现。我可以像下面这样来实现:

      #ruby // 深红色的例子
      puts 'What is your name?' // 发送请求
      name = gets               // 获取输入的名称
      process_name(name)        // 处理名称 
      puts 'What is your quest?'
      quest = gets
      process_quest(quest)

        在这种交互中,我的代码在控制:什么时候发送请求,什么时候获取返回的结果以及什么时候处理返回的结果。

        但是,如果我使用一个窗口系统来完成这件的事,我可以配置一个窗口来实现: 

      require 'tk'
      root = TkRoot.new()
      name_label = TkLabel.new() { text "What is Your Name?" }   // 新建Label控件,用来显示请求文本
      name_label.pack                                            
      name = TkEntry.new(root).pack                              // 新建文本输入框控件
      name.bind("FocusOut") { process_name(name) }               // 给文本输入框绑定失去焦点事件,用来处理名称
      quest_label = TkLabel.new() { text "What is Your Quest?" }
      quest_label.pack
      quest = TkEntry.new(root).pack
      quest.bind("FocusOut") { process_quest(quest) }
      Tk.mainloop()                                               // 循环以上代码

        这些程序的控制流之间存在很大的差别,尤其是在控制 process_name 和 process_quest 方法的调用这点上。在命令行的例子中,我直接控制这些方法什么时候被调用。但是在窗口系统的例子中,我没有这样做。相反,我手动控制的是窗口系统(利用 Tk.mainloop 方法),然后由 Tk.mainloop 方法根据我在创建窗体时所绑定的事件,来决定什么时候调用 process_name 和process_quest 方法。在这里,控制是倒置的——框架调用我写的方法,而不是我来调用框架里的方法。这种现象就是控制反转(也被称为好莱坞原则——“ 不要打电话给我们,我们会打电话给你 ”)。 

        框架的一个重要特征是:用户自己写了一些方法(常常会调用框架中已经定义过的方法),这些方法常常是被框架内部程序所调用,而不是被用户自己的应用程序所调用。调试和测试应用程序时,框架经常是其中的主要程序。有了控制反转模式,框架有权成为可扩展的核心骨架。而用户提供的方法,则是为特定的应用程序,定制了一套框架中已存在的通用逻辑。

    —— Ralph Johnson 和 Brian Foote  

        框架区别于程序集一个关键因素就是控制反转。程序集基本上是由一组可以调用的函数组成,这些函数常常被组织成类。每次调用函数时,程序会做一些处理,并将控制权返回给客户端。一个框架则包含了一些抽象的设计,和许多内置的行为。为了使用它,首先你需要在框架的不同的地方,通过子类继承或构造自己的类并添加你的方法。然后框架会在相应的地方来调用你写的这些方法的代码。

        当然,你可以使用多种不同的方法来添加等待被调用的方法。在上面深红色部分的例子中,我们调用文本框输入框上的绑定方法(bind),为它传递了一个事件名称(这里是 FocusOut 事件)和一个 λ 表达式 参数(process_name(name))。每当文本输入框的检测到 FocusOut 事件时,它会调用闭包中的代码。在这里使用闭包非常方便,不过很多语言不支持闭包。 

        另一种方法是在框架中定义各种事件,然后让客户端代码订阅这些事件。在 .NET 框架中就支持这种方式:它允许开发者在控件上定义不同的事件,并将需要方法写到控件的事件代码中,最后通过委托来调用这些方法。

     

        上面的方法(本质上它们是一样的)对单个例子适用,如果你需要在一个单一的单元里,组合一些必须的方法来调用,又该怎么办呢?这时,你可以定义一个接口,客户端代码通过实现该接口来获取不同的方法调用。在这方面,EJBs 就是一个很好的例子而且还运用了控制反转。当开发一个 bean 会话时,你可以先实现各种可以被 EJB 容器调用的方法,这些方法将在容器生命周期的不同时间点上被调用。例如 Bean 会话接口定义了 ejbRemove,ejbPassivate(存储到二级存储)和 ejbActivate (从被动状态恢复)等,你没有控制这些方法什么时候被调用,只是控制它们做什么操作。它们由容器调用,而不是由我们来调用。 

        以上是控制反转中的比较复杂的例子,当你碰到类似的简单得多的情况时,你将会感觉到。模板方法 也是一个很好的例子:父类定义控制流,子类通过重写或实现抽象方法来扩展它。因此在JUnit (java 单元测试)中,框架框架代码调用 setUp 和 tearDown 方法来帮你创建和拆卸文本固定装置。当它被调用时,你写的代码作出具体的反应——所以控制也是反转的。 

        近来,由于 IoC 容器的出现,控制反转的定义又让一些人产生了迷惑。这些人是把控制反转的一般原则和 IoC 容器使用的具体实现方式(例如 依赖注入)弄混淆了。这个名字有点混乱(也有点讽刺),因为 IoC 容器通常被视为 EJB 的竞争对手,但 EJB 同样也使用了控制反转(如果没有其他更多的选择的话)。 

     

    “控制反转”一词的来源: 

        据我所知,“ 控制反转 ” 这个术语最早出现在面向对象编程杂志1988年发表的 Designing Reusable Classes 一文中。这是一篇经得起时间考验的经典的文章,直到15年以后的今天,它仍然具有很好的阅读价值。他们那时也是从其他地方得到的这个术语,不过他们记不清是从哪里得到的。该术语表明它来源于属于面向对象编程社区,同时在 Gang of Four  一书中出现过。

        那个形象生动的解释 “ 好莱坞原则 ” 似乎来源于1983年 Richard Sweet 写的一篇论文 。在一个设计目标的列表单中,他写道:“ 不要给我们打电话,我们会打电话给你(好莱坞定律):当 Tajo 希望通过工具来传达一些事件时,应该为该用户安排一个通知工具,而不是采取一种 ‘ 要求用户提供一个命令并执行它 ’ 的模式 ”。 John Vlissides 写的 C++ 专栏 中,对这一概念提供了一个很好的解释,并运用了 “ 好莱坞原则 ” 。(感谢 Brian Foote 和 Ralph Johnson 帮助我寻找词源。)

  • 相关阅读:
    将内容重定向到剪切板(clip.exe)
    加速数组操作(Array)
    错误信息输出,重定向到文件
    格式化数字字符串
    PowerShell常用的.Net 、COM对象(New-Object、Assembly)、加载程序集
    计算文件夹大小、拷贝文件显示进度
    草稿-Hyper-V
    右下角显示提示窗口(New-Object,COM)
    《TCP/IP详解卷一:协议》数据链路层(一)
    tcpdump抓包命令
  • 原文地址:https://www.cnblogs.com/hellowzl/p/5718645.html
Copyright © 2020-2023  润新知