在Ruby中装饰方法

时间:2016-06-01 04:06:45

标签: ruby design-patterns

我有一个方法,它将一个整数数组作为参数,我想使用装饰器模式首先验证数组中的每个数字是否在指定的范围内。

我在ruby中看过装饰器扩展类或包含模块,但这对我来说似乎有点过分,有没有办法在没有中继类或模块的情况下装饰方法?

修改 我有几个方法将数组作为参数,并需要验证项的范围。我不想在每个方法中对内部编码进行内联编码,而是想要所有这些方法的装饰器,但我想知道装饰器类/模块是否只是Ruby中存在的那些?

class MyClass
 ..some code here ...
 def method(array)
   ..some code here...
 end
end

2 个答案:

答案 0 :(得分:5)

这是一个如何使用验证器包装方法的简单示例:

class Foo
  def foo(a, b, c)
    p a, b, c
  end

  def bar(a, b)
    p a, b
  end

  validate(:foo, :bar) {|*args, &blk| args.reduce(:+) == 6 } 
end

Module#validate方法获取方法名称列表,以及具有这些方法验证逻辑的块。

foo = Foo.new

foo.foo(1, 2, 3)
# 1
# 2
# 3

foo.bar(2, 4)
# 2
# 4

foo.foo(2, 3, 4)
# [2, 3, 4] (Validator::ValidationError)

foo.bar(2, 3)
# [2, 3] (Validator::ValidationError)

如您所见,验证器在最后两次调用中拒绝了参数列表,因为它们没有在块中传递条件。

这是"魔法"这一切都发生了,实际上并不是那么神奇。我们生成一个module,它覆盖了我们要验证的方法,如果验证失败,raise是一个例外,那么只需调用super。然后我们prepend该模块到我们当前所在的类/模块,即调用validate方法的模块。基本上就是这样。

我选择成为一名优秀的红宝石公民并将整个事情包装在精炼中,所以你需要说

using Validator

实际激活它。

module Validator
  class ValidationError < ArgumentError; end

  refine Module do
    def validate(*methods, &validator)
      prepend(Module.new do
        methods.each do |method|
          define_method(method) do |*args, &blk|
            raise ValidationError, args.inspect unless yield *args
            super(*args, &blk)
          end
        end
      end)
    end
  end
end

答案 1 :(得分:3)

通常,当你谈论装饰时,你会在课程的上下文中使用它,而不是单一的方法。

以下是您可以装饰的课程示例:

class ValidatorSet
  def initialize
    @validators = [ ]
  end

  def <<(validator)
    @validators << validator
  end

  def valid?(list)
    @validators.all? do |v|
      if v.respond_to?(:valid?)
        v.valid?(list)
      else
        list.all?(&v)
      end
    end
  end
end

class IsUnderTen
  def valid?(list)
    list.all? { |n| n < 10 }
  end
end

validator = ValidatorSet.new
validator << IsUnderTen.new
validator << lambda { |n| n > 0 }

validator.valid?([ 1, 2, 3, 4, 5 ])
# => true

validator.valid?([ -1 ])
# => false

validator.valid?([ 9, 10, 11, 12 ])
# => false

你会在ActiveRecord验证链中看到这种东西。