4

我想知道 Ruby 中创建自定义 setter 和 getter 方法的规范方法是什么。通常,我会这样做,attr_accessor但我是在创建 DSL 的上下文中。在 DSL 中,setter 是这样调用的(使用=符号将创建局部变量):

work do
 duration 15
 priority 5
end

因此,它们必须像这样实现:

def duration(dur)
 @duration = dur
end

然而,这使得实现 getter 有点棘手:创建一个具有相同名称但没有参数的方法只会覆盖 setter。

所以我编写了自定义方法来进行设置和获取:

def duration(dur=nil)
 return @duration = dur if dur
 return @duration if @duration
 raise AttributeNotDefinedException, "Attribute '#{__method__}' hasn't been set"
end

这是一个很好的方法吗?这是测试用例的要点:

Ruby 自定义 Getter 和 Setter

谢谢!

4

4 回答 4

9

更棘手的情况是,如果您想将持续时间设置为零。我可以想到两种方法

def duration(*args)
  @duration = args.first unless args.empty?
  @duration
end

允许人们传递任意数量的参数并根据数量决定做什么。如果传递了多个参数,您也可以引发异常。

另一种方法是

def duration(value = (getter=true;nil))
  @duration = value unless getter
  @duration
end

这稍微利用了默认参数:它们几乎可以是任何表达式。

当不带参数调用时getter设置为 true,但是当提供参数时(即使它是 nil),默认值不会被计算。由于局部变量作用域的工作getter方式最终为零。

可能有点聪明了,但方法体本身更干净。

于 2012-07-02T16:20:52.580 回答
2

对于这样的事情,我更喜欢将底层类与 DSL 分开。也就是说,创建一个具有常用访问器的 Work 类,duration并且duration=. 要通过 DSL 使用该类,请使用可以根据情况调用访问器的东西来包装工作实例,如下所示:

class AccessorMultiplexer

  def initialize(target)
    @target = target
  end

  def method_missing(method, *args)
    method = "#{method}=" unless args.empty?
    @target.send method, *args
  end

end

无论您想通过 DSL 在何处使用您的 Work 类,都可以使用AccessorMultiplexer.new(work).

如果您反对在包装器中进行元编程,您总是可以制作一个特定的 WorkDSL 包装器,它可以在不使用method_missing. 但它会保持分离并防止您的 Work 类被 DSL 语法的怪癖污染。您可能希望在代码中的其他地方使用 Work 类,而 DSL 会妨碍您。在 rake 或脚本中,或者——谁知道呢。

(改编自我对codereview的回答。)

于 2012-07-02T16:21:17.353 回答
0

这对我来说似乎很好,尽管如果尚未设置值,您会引发错误似乎很奇怪。这是我通常会做的:

def duration(dur=nil)
 @duration = dur if dur
 @duration
end

然而,这是一种简单的方法,因为这意味着您不能@durationnil使用这种方法

于 2012-07-02T16:13:25.180 回答
0

我通常这样做

def duration dur='UNDEFINED'
  @duration = dur if dur != 'UNDEFINED'
  @duration
end

你可以用你最喜欢的东西代替 UNDEFINED

于 2012-08-14T13:57:20.037 回答