Inheriting/sharing 代码跨两个 Ruby 模块

Inheriting/sharing code across two Ruby modules

这里是菜鸟,正在寻求帮助来解决这个问题。假设我有 module DoAThing 看起来像这样:

module DoAThing
  class ExportNames
    def initialize(names, limit = 1000)
      @names = names
      @limit = limit
    end

    def perform
      awesome = ::ClassAwesome.sort_names(names, per_page: @limit)
      awesome.generate_export(::ClassAwesome::EXPORT_DEFAULTS)
    end
  end
end

然后我想添加一个模块来做完全相同的事情,除了在生成报告时,我想传递一个不同的常量,所以像这样:

module DoASlightDifferentThing
  class ExportNames
    def initialize(names, limit = 1000)
      @names = names
      @limit = limit
    end

    def perform
      awesome = ::ClassAwesome.sort_names(names, per_page: @limit)
      awesome.generate_export(::ClassAwesome::DIFFERENT_EXPORT_DEFAULTS)
    end
  end
end

我怎样才能优雅地把它擦干?从字面上看,我需要做的就是传入一个不同的常量,即我想将 EXPORT_DEFAULTS 换成 DIFFERENT_EXPORT_DEFAULTS

这通常被称为“依赖注入”,互联网上有很多关于这个主题的文章。主要方法是在调用 #perform 方法时接受一个参数:

module DoManyDifferentThings
  class ExportNames
    def initialize(names, limit = 1000)
      @names = names
      @limit = limit
    end

    def perform(report)
      awesome = ::ClassAwesome.sort_names(names, per_page: @limit)
      awesome.generate_export(report)
    end
  end
end

并这样称呼它:

DoManyDifferentThings::ExportNames.perform(
  ::ClassAwesome::EXPORT_DEFAULTS
)

报告方法是否应该有一个预定义的签名,一个可能与 class 继承:

module DoManyDifferentThings
  class ExportNames
    def initialize(names, limit = 1000)
      @names = names
      @limit = limit
    end

    def perform
      awesome = ::ClassAwesome.sort_names(names, per_page: @limit)
      awesome.generate_export(self.class.constant_get('REPORT'))
    end
  end

  class ExportNames1 < ExportNames
    REPORT = ::ClassAwesome::EXPORT_DEFAULTS_1
  end

  class ExportNames1 < ExportNames
    REPORT = ::ClassAwesome::EXPORT_DEFAULTS_2
  end
end

或者,作为替代方案,可以声明一种方法在后代中被覆盖:

module DoManyDifferentThings
  class ExportNames
    def initialize(names, limit = 1000)
      @names = names
      @limit = limit
    end

    def perform
      awesome = ::ClassAwesome.sort_names(names, per_page: @limit)
      awesome.generate_export(report)
    end

    protected

    def report
      raise "DO NOT CALL ME DIRECTLY"
    end
  end

  class ExportNames1 < ExportNames
    def report
      ::ClassAwesome::EXPORT_DEFAULTS_1
    end
  end

  class ExportNames2 < ExportNames
    def report
      ::ClassAwesome::EXPORT_DEFAULTS_2
    end
  end
end

如果 类 的命名空间可以相同,那么我会通过重写这样的代码来清理代码

module DoAThing
  class ExportNames
    def initialize(names, limit = 1000)
      @names = names
      @limit = limit
    end

    def perform(value)
      awesome = ::ClassAwesome.sort_names(names, per_page: @limit)
      awesome.generate_export(value)
    end
  end
end 

现在我会打电话给

DoAThing::ExportNames.new('your names').perform(::ClassAwesome::EXPORT_DEFAULTS)
DoAThing::ExportNames.new('your names').perform(::ClassAwesome::DIFFERENT_EXPORT_DEFAULTS)

您可以将此常量作为参数移动,也可以使用 ihneritance。

提供默认值作为参数

module DoAThing
  class ExportNames
    def initialize(names, limit = 1000, defaults = ::ClassAwesome::EXPORT_DEFAULTS)
      @names = names
      @limit = limit
      @defaults = defaults
    end

    def perform
      awesome = ::ClassAwesome.sort_names(names, per_page: @limit)
      awesome.generate_export(@defaults)
    end
  end
end

不是调用 DoASlightDifferentThing::ExportNames,而是调用:

DoAThing::ExportNames.perform_now(names, 1000, ::ClassAwesome::DIFFERENT_EXPORT_DEFAULTS)

知识

或者您可以使用 ihneritance,我们在其中添加一个将被覆盖的默认方法:

module DoAThing
  class ExportNames
    def initialize(names, limit = 1000)
      @names = names
      @limit = limit
    end

    def perform
      awesome = ::ClassAwesome.sort_names(names, per_page: @limit)
      awesome.generate_export(defaults)
    end

    def defaults
      ::ClassAwesome::EXPORT_DEFAULTS
    end
  end
end

module DoASlightDifferentThing
  class ExportNames < DoAThing::ExportNames
    def defaults
      ::ClassAwesome::DIFFERENT_EXPORT_DEFAULTS
    end
  end
end