简体   繁体   English

使用Ruby替换运行时实现

[英]Runtime implementation substitution with Ruby

Dependency Injection frameworks in Ruby have been pretty much declared unnecessary. Ruby中的依赖注入框架几乎被宣布为不必要的。 Jamis Buck wrote about this last year in his LEGOs, Play-Doh, and Programming blog post. Jamis Buck去年在他的LEGO,Play-Doh和Programming博客文章中写到了这一点。

The general accepted alternative seems to be using some degree of constructor injection, but simply supplying defaults. 普遍接受的替代方案似乎是使用某种程度的构造函数注入,但只是提供默认值。

class A
end

class B
  def initialize(options={})
    @client_impl = options[:client] || A
  end

  def new_client
    @client_impl.new
  end
end

This approach is fine by me, but it seems to lack one thing from more traditional setups: a way of substituting implementations at runtime based on some external switch. 这种方法对我来说很好,但似乎缺少一些来自更传统的设置:一种在运行时基于某些外部交换机替换实现的方法。

For example with a Dependency Injection framework I could do something like this (pesudo-C#): 例如,使用依赖注入框架,我可以做这样的事情(pesudo-C#):

if (IsServerAvailable)
  container.Register<IChatServer>(new CenteralizedChatServer());
else
  container.Register<IChatServer>(new DistributedChatServer());

This example just registers a different IChatServer implementation depending on whether our centeralized server is available. 此示例仅根据我们的IChatServer服务器是否可用来注册不同的IChatServer实现。

As we're still just using the constructor in Ruby, we don't have programatic control over the dependencies that are used (unless we specify each one ourselves). 由于我们仍然只是在Ruby中使用构造函数,因此我们没有对所使用的依赖项进行编程控制(除非我们自己指定每个依赖项)。 The examples Jamis gives seem well suited to making classes more testable, but seem to lack the facilities for substitution. Jamis提供的示例似乎非常适合使类更易于测试,但似乎缺少替换设施。

What my question is, is how do you solve this situation in Ruby? 我的问题是,你是如何在Ruby中解决这种情况的? I'm open to any answers, including "you just don't need to do that". 我愿意接受任何答案,包括“你根本不需要这样做”。 I just want to know the Ruby perspective on these matters. 我只是想了解Ruby对这些问题的看法。

In addition to constructor substitution, you could store the information in an instance variable ("attribute"). 除了构造函数替换之外,您还可以将信息存储在实例变量(“attribute”)中。 From your example: 从你的例子:

class A
end

class B
  attr_accessor :client_impl

  def connect
    @connection = @client_impl.new.connect
  end
end

b = B.new
b.client_impl = Twitterbot
b.connect

You could also allow the dependency to be made available as an option to the method: 您还可以允许依赖项作为方法的选项提供:

class A
end

class B
  def connect(impl = nil)
    impl ||= Twitterbot
    @connection = impl.new.connect
  end
end

b = B.new
b.connect

b = B.new
b.connect(Facebookbot)

You could also mix-and-match techniques: 你也可以混合搭配技巧:

class A
end

class B
  attr_accessor :impl

  def initialize(impl = nil)
    @impl = impl || Twitterbot
  end

  def connect(impl = @impl)
    @connection = impl.new.connect
  end
end

b = B.new
b.connect # Will use Twitterbot

b = B.new(Facebookbot)
b.connect # Will use Facebookbot

b = B.new
b.impl = Facebookbot
b.connect # Will use Facebookbot

b = B.new
b.connect(Facebookbot) # Will use Facebookbot

Basically, when people talk about Ruby and DI, what they mean is that the language itself is flexible enough to make it possible to implement any number of styles of DI without needing a special framework. 基本上,当人们谈论Ruby和DI时,他们的意思是语言本身足够灵活,可以在不需要特殊框架的情况下实现任意数量的DI风格。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM