【问题标题】:What is a clean interface to set class variables in a mixin module?什么是在 mixin 模块中设置类变量的干净接口?
【发布时间】:2013-07-12 21:50:40
【问题描述】:

在我正在处理的一个 Ruby 项目中,我将 ActiveRecord 风格的 MVC 功能添加到具有类似于以下混合架构的模型类中:

module Model

  # Classes that mixin this module gain ActiveRecord-style class methods
  # like Model.all, Model.first, Model.last et al.
  #
  # Throughout this module, @@database_bridge will contain a reference to a
  # database ORM bridge, that does the dirty implementation of these methods.

  def all
    # Implementation stuff here, using @@database_bridge as appropriate
  end

  def first
    ...
  end

  # et al

end


class ExampleModel

  extend Model

  # Model-specific implementation goes here...

end

调用e = ExampleModel.first 会将数据库中的第一个ExampleModel 分配给e。

我想在运行时使用依赖注入来设置@@database_bridge,这样每个包含extend Model 的类都使用相同的指定ORM 对象。

我该怎么做?

如果我可以编写某种辅助方法来按需设置该类变量,那就太好了。

【问题讨论】:

  • 如果有人认为我最好从基本的“模型”类继承东西,那真的不适合我的类层次结构。该框架的核心不是 MVC,它只是使用 MVC 样式的类方法作为从数据库中提取内容的一种优雅方式。

标签: ruby dependency-injection mixins class-variables


【解决方案1】:

这不是答案,而是一个潜在的解决方案:您可以在一个模块上调用class_variable_set,方法是调用Module.class_variable_set。

因此,您可以在某个适当的命名空间中创建一个调用 Module.class_variable_set :@@class_var, "new value" 的辅助方法。

对于上面的例子,我的辅助函数是这样的:

def set_database_bridge(new_bridge)
  Model.class_variable_set :@@database_bridge, new_bridge
end

这种解决方案在辅助函数和Model mixin 的实现之间产生了一定程度的耦合,好像@@database_bridge 的名称要更改,辅助函数就会中断。

如果有人对更松散耦合/更封装的解决方案有想法(我们可能将辅助函数封装在 Model 的某个地方),那就太棒了!

【讨论】:

    【解决方案2】:

    我找到了比以前更好的解决方案,而且比我想象的要简单得多(d'oh!):通过在 mixin 模块中为方法添加 self. 前缀,该方法的公共接口通过Module.method 变为可用。

    因此,我们只需使用self.attribute_set 语句向我们的模块添加一个setter。

    在上面的例子中,上面的方法会产生以下代码:

    module Model
    
      # Classes that mixin this module gain ActiveRecord-style class methods
      # like Model.all, Model.first, Model.last et al.
      #
      # Throughout this module, @@database_bridge will contain a reference to a
      # database ORM bridge, that does the dirty implementation of these methods.
    
      def all
        # Implementation stuff here, using @@database_bridge as appropriate
      end
    
      def first
        ...
      end
    
      def self.set_database_bridge(ref_to_new_bridge)
        @@database_bridge = ref_to_new_bridge
        ## any additional intialisation/sanitisation logic goes here
      end
    
      # et al
    
    end
    
    
    class ExampleModel
    
      extend Model
    
      # Model-specific implementation goes here...
    
    end
    

    调用Model.set_database_bridge 将允许我们传入一个新的数据库桥。

    如果我们在帮助函数中实际上不需要任何初始化或清理逻辑,还有另一种更优雅的方法 - 在 class << self 块中添加 attr_accessor,因此:

    module Model
    
      # ...
    
      class << self
        attr_accessor :database_bridge
      end
    
    end
    

    这样,我们可以调用 Ruby 的标准 setter 方法:Model.database_bridge = the_new_bridge。

    甜蜜。

    【讨论】:

    • 这确实是我一直在寻找的答案,如果没有人对这种方法有任何特定的“陷阱”,明天就会打勾。欢迎提出意见和建议!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-18
    • 2012-03-13
    • 1970-01-01
    相关资源
    最近更新 更多