【问题标题】:"The Ruby way" (mixins and class reopening) vs. dependency injection“Ruby 方式”(混合和类重新打开)与依赖注入
【发布时间】:2011-10-27 06:41:33
【问题描述】:

在研究 mixins 与依赖注入时,我经常听到“Ruby 方式”这个短语。开发人员经常会说类似

Ruby 允许您重新打开类并重新定义方法,这意味着您可以 在测试时轻松地将新引用“注入”到您的代码中。

(参见#6 http://weblog.jamisbuck.org/2007/7/29/net-ssh-revisited

但测试不是我主要关心的;我关心的是类重用。我想要可以在多个企业级 Rails 应用程序中重用的类。

那么重用类发生了什么?使用 mixins 和重新打开类似乎并没有提供一种编写类的方法,即无需大量额外工作即可将它们与特定于应用程序的细节分离。但也许我错了。如果我是,有人可以提供一个包含示例代码的文章的链接,该文章清楚地解释了如何使用 mixin 和重新打开类来正确完成此操作?

举个例子,这里的Foo类耦合到Logger类:

class Foo
  def initialize
    @logger = new_logger
  end

  def new_logger
    Logger.new
  end
end

是的,我可以重新打开 Foo 并重新定义 new_logger,但我无法相信这被认为是编写可供多个 Rails 应用程序使用的可重用类的现实标准方法。

【问题讨论】:

    标签: ruby-on-rails ruby dependency-injection mixins


    【解决方案1】:

    关于如何在Ruby中使用IoC的文章You underestimate the power of IoC

    带有工作样本。

    更新

    链接已经失效,这里是源https://github.com/alexeypetrushin/rubylang/blob/master/draft/you-underestimate-the-power-of-ioc.md

    我将该 IoC 用作我的 Web 框架的一部分,我重新创建了 Ruby on Rails 并且它工作了,但是,它不会比 RoR 有明显的优势,具有相似的特征。所以,它成了一个负担,我放弃了它,一些细节http://petrush.in/blog/2011/rad-web-framework

    【讨论】:

    • 该链接似乎已失效 :( 只是想指出它,即使该线程已旧,因为我会对此阅读感兴趣。
    • Here 是相关链接的存档版本。
    【解决方案2】:

    我们可以在 Ruby 中重新打开类并不意味着我们总是必须这样做,您可以将重新打开类视为不得已的方法。你有一个库,除了一个方法之外,你需要做的一切,而不是分叉整个库,修补它并使用你的 fork,你可以简单地重新打开类,重新定义方法,然后你就可以重新开始工作了。这不是你会做的事,但能够做到这一点非常有用。

    说了这么多,在 Ruby 中,我们有一个几乎总是可以很好地替代依赖注入的概念——鸭子类型。由于没有类型检查,您可以将任何对象传递给函数,并且只要对象具有函数期望的方法,一切都会正常工作。

    让我们看看你的例子——它并不是真正有利于依赖注入的类,你不会在 Java 中这样写,例如,如果你想注入一些依赖项。您只能通过构造函数或通过 getter 和 setter 注入。所以让我们这样重写这个类:

    class Foo
      def initialize(logger)
        @logger = logger
      end
    end
    

    更好的是,我们现在可以将记录器注入/传递到我们的 Foo 类中。让我们添加一个方法来使用这个记录器来演示:

    class Foo
      def initialize(logger)
        @logger = logger
      end
    
      def do_stuff
        @logger.info("Stuff")
      end
    end
    

    在java中,如果你想用不同类型的记录器创建Foo对象,那么所有这些记录器都必须在字面上实现相同的接口(例如public class logger implements Loggable),或者至少是子类。但是在 Ruby 中,只要对象有一个接受字符串的 info 方法,您就可以将它传递给构造函数,并且 Ruby 会继续愉快地前进。让我们演示一下:

    class Logger
      def info(some_info)
      end
    end
    
    class Widget
      def info(some_widget_info)
      end
    end
    
    class Lolcat
      def info(lol_string)
      end
    end
    
    Foo.new(Logger.new).do_stuff
    Foo.new(Widget.new).do_stuff
    Foo.new(Lolcat.new).do_stuff
    

    以上所有 3 个 Foo 类实例都调用了 do_stuff 方法,一切都会正常工作。

    从这个例子中你可以看到,坚持面向对象设计的原则仍然很重要,但是 Ruby 对它所接受的内容的限制要少一些,只要有正确的方法,一切都会好起来的。

    根据你的看法,鸭子类型要么使依赖注入完全无关紧要,要么使其比以往更强大。

    【讨论】:

    • 仍然必须注入依赖,不管类型是否重要,所以我个人不得不说依赖注入似乎仍然相关。
    • 我宁愿说依赖倒置仍然相关,注入则不那么重要。如果不先进行一些自省,就很难解释清楚,但事实是,随着您编写的 Ruby 代码越来越多,并且越来越擅长它,您对依赖注入的需求就会减少。也许是因为您可以用更少的代码完成相同数量的工作,因此库往往更小。
    【解决方案3】:

    实际上,当我从 Java 世界来到 ruby​​ 世界时,我感兴趣的第一件事就是他们如何管理依赖关系。那时我在所有 Java 项目中都使用 Google Guice,我真的被它巧妙的设计和易用性所启发。我在 ruby​​ 中做的第一件事是我自己的 DI 容器,它具有与 Google Guice 大致相同的功能集 - (it is still here on github,但它非常过时)。

    但是现在在使用 Rails/Ruby 2 年后,我认为这里不需要 DI。关于 DI 在 ruby​​ 中的好文章是 http://weblog.jamisbuck.org/2008/11/9/legos-play-doh-and-programming 它实际上是一篇关于为什么 DI 不是第一个 DI 容器的作者需要 DI 的文章。绝对值得一读。

    【讨论】:

      【解决方案4】:

      简单的答案是,Ruby 语言中没有任何东西可以阻止您编写可重用的类。使用混合和类重新打开的常用方法不一定会促进它,但该语言实际上并不阻止其他方法。将“Ruby 方式”视为“Ruby 可以做的事情”的子集

      也就是说,我知道通常最好使用语言结构来强制执行设计决策,并且据我所知(我会警告您在该主题上还远未完成)DI 目前还不是核心 Ruby 主义。不过,通过谷歌搜索我发现了一些关于该主题的文章,这让我相信如果你愿意的话,可以找到一些库来将 DI 添加到 Ruby 中(尽管它们似乎受到了许多 Ruby 主义者的批评)。信息性文章包括 these twothis SO question。希望对您有所帮助。

      【讨论】:

      【解决方案5】:

      我完全同意。动态语言不能替代依赖注入。没有什么能阻止你为动态语言编写一个。这是 Smalltalk 的依赖注入框架:http://www.squeaksource.com/Seuss.html

      【讨论】:

      • 其实重定义方法就是类似classbox的本地重绑定。 Seuss 和 Classbox 有什么重叠之处?
      猜你喜欢
      • 1970-01-01
      • 2012-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-16
      • 2010-09-21
      • 2011-11-12
      • 2011-12-16
      相关资源
      最近更新 更多