2

我有两个类 A 和 B,它们都有一些选项,通常我会使用一个 Hash 来存储选项,例如@options[:name]='xxxx';现在我想用元编程重构它,

class A
    set_option :name, "james"
    set_option :address, "some street"

    def hello
        puts @options[:name]
        puts @options[:address]
    end
end


class B
    set_option :age, 18

    def greeting
        put @options[:age]
    end
end 

在这里,我想使用set_option将键值对设置为一个哈希实例@options,我该怎么做?

此外,我想将解决方案包装到一个单独的模块中。

更新:

首先谢谢,你所有的答案对我来说都很有价值,让我更清楚,现在我意识到我想要的一些东西不是那么正确,那么如果我提出这样的问题呢?

  • 如何使用一种方法option来替换@options
  • A 类和 B 类都可以为同一个键设置不同的选项,所以可能有些答案@@options不起作用?我希望不同的类可以有不同的哈希实例。

``` 像这样:

class A
    set_option :name, "james"
    set_option :provider, 'twitter'

    def hello
        puts option[:name]
    end
end


class B
    set_option :name, "not james"

    def greeting
        put option[:name]
    end
end 

经过深思熟虑,我认为我真正想要的是不同类的不同选项哈希实例,而不是类的实例。

这是我想要的,它可以工作。

module HasOptions
  def self.included(cls)
    cls.class_eval do
      def self.set_option(key, value)
        options[key] = value
      end

      def self.options
        @options ||= {}
      end

      def options
        self.class.options
      end
    end
  end
end


class Baz
  include HasOptions
  set_option :name, "bad"

  def greeting
        puts options[:name]
  end
end

class Foo
  include HasOptions
  set_option :name, "foo"

  def greeting
        puts options[:name]
  end
end

感谢你的帮助。

4

3 回答 3

5

正如您尝试做的那样,使用实例变量是行不通的。但是,您可以使用这样的方法:

module HasOptions
  def self.included(cls)
    cls.class_eval do
      def self.set_option(key, value)
        (@@options ||= {})[key] = value
      end
    end
  end

  def options
    @options ||= @@options.dup
  end
end

此实现允许您设置每个实例的选项,而不会覆盖所有实例的通用选项。

于 2013-02-17T08:06:28.623 回答
2

看起来你有点搞砸了。让我试着列出你想要解决的问题:

  • 将选项存储在类变量的实例中Hash(我猜对了吗? - 一个用于所有类实例的哈希);
  • 使用漂亮的符号来设置选项。

上述任何内容实际上都不需要元编程。要满足第一个条件,您只需找到所有类的第一个共同祖先并 [monkey] 用您的@@optionsvar 和set_option方法修补它。如果你没有决定为你的所有类提供你自己的超类,让我们修补最祖先(例如Kernel模块):

module Kernel
  def set_option name, value
    (@@options ||= {})[name.to_sym] = value
  end
end

现在任何类都可以包含set_option将选项放入共享选项哈希的“指令”(考虑使用实例变量@options而不是@@options使选项特定于实例,或者使用 Alex D 的解决方案。)

如果您想为选项设置使用花哨的花哨语法(使用默认值、就地检查器、褶边和奢侈品),您将在此处使用DSL本文中的第一个示例展示了如何set_option使用 DSL 实现 pure。进一步的调整仅受您的想象力限制。

于 2013-02-17T10:14:50.420 回答
1

如果您需要初始化实例选项,您应该使用实例方法来执行此操作。

module OptionSetter
  def set_option(key, value)
    @options[key] = value
  end
end

class Base
  def initialize(options = {})
    @options = {}
    options.each do |key, value|
      set_option key, value
    end
  end
end

class A < Base
  include OptionSetter

  def hello
    puts @options[:name]
    puts @options[:address]
  end
end

class B < Base
  include OptionSetter

  def greeting
    puts @options[:age]
  end
end

A.new(name: "james", address: "some street").hello
B.new(age: 18).greeting

在 ruby​​ 中更常见的方法是使用attr_accessor's

class Base
  attr_accessor :options

  def initialize(options = {})
    @options = options
  end
end

class A < Base
  def hello
    puts @options[:name]
    puts @options[:address]
  end
end

class B < Base
  def greeting
    puts @options[:age]
  end
end

A.new(name: "james", address: "some street").hello
B.new(age: 18).greeting

# another approach
james = A.new
james.options[:name] = "james"
james.options[:address] = "some street"
james.hello
于 2013-02-17T08:15:04.527 回答