代码之家  ›  专栏  ›  技术社区  ›  James Gregory

用Ruby替换运行时实现

  •  6
  • James Gregory  · 技术社区  · 15 年前

    LEGOs, Play-Doh, and Programming 博客文章。

    普遍接受的替代方案似乎是使用某种程度的构造函数注入,但只是提供默认值。

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

    这种方法对我来说很好,但它似乎缺少一件传统设置中的东西:一种在运行时基于一些外部开关替换实现的方法。

    例如,使用依赖注入框架,我可以做类似的事情(pesudo-C#):

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

    这个例子只是注册了一个不同的 IChatServer

    由于我们仍然只是在Ruby中使用构造函数,我们对所使用的依赖项没有编程控制(除非我们自己指定每个依赖项)。Jamis给出的示例似乎非常适合使类更易于测试,但似乎缺少替换的功能。

    1 回复  |  直到 15 年前
        1
  •  9
  •   Yehuda Katz    15 年前

    除了构造函数替换之外,还可以将信息存储在实例变量(“属性”)中。从你的例子来看:

    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
    

    您还可以允许依赖项作为方法的一个选项提供:

    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)
    

    您还可以混合搭配技术:

    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
    

    基本上,当人们谈论Ruby和DI时,他们的意思是语言本身足够灵活,可以在不需要特殊框架的情况下实现任意数量的DI样式。