Ruby Module的include钩子代替常规的扩展?

问题描述 投票:0回答:1

有一种通用的方法是通过Module的include钩子从Module中添加类方法,然后用ClassMethods子模块扩展基类。这种方法在《Metaprogramming Ruby 2: Program Like the Ruby Pros》一书中有介绍。下面是其中的一个例子。

module CheckedAttributes

  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods

    def attr_checked(attribute, &validation)

      define_method "#{attribute}=" do |value|
        raise 'Invalid attribute' unless validation.call(value)
        instance_variable_set("@#{attribute}", value)
      end

      define_method attribute do
        instance_variable_get "@#{attribute}"
      end
    end
  end
end

class Person
  include CheckedAttributes
  attr_checked :age do |v|
    v >= 18
  end
end

但为什么要先包含几乎是空的模块,然后再用一个模块来扩展它的includer呢?为什么不直接用目标模块本身来扩展类呢?

module CheckedAttributes

  def attr_checked(attribute, &validation)

    define_method "#{attribute}=" do |value|
      raise 'Invalid attribute' unless validation.call(value)
      instance_variable_set("@#{attribute}", value)
    end

    define_method attribute do
      instance_variable_get "@#{attribute}"
    end
  end
end

class Person
  extend CheckedAttributes

  attr_checked :age do |v|
    v >= 18
  end
end

上面的代码是否完全等同于这本书中的初始例子?还是有什么陷阱?

ruby-on-rails ruby oop metaprogramming
1个回答
2
投票

我不知道你是从哪里得到这段代码的,但是这个模式涉及到了 ClassMethods 通常用于您要改变 两者 类和特征类,以避免必须同时调用两个 include Fooextend Bar.

module Named
  def self.included(base)
    base.extend ClassMethods
  end

  def describe
    "Person is: #{name}"
  end

  module ClassMethods
    def name!
      define_method "name=" do |value|
        raise 'Invalid attribute' unless validation.call(value)
        instance_variable_set("@name", value)
      end

      define_method "name" do
        instance_variable_get "@name"
      end
    end
  end
end

class Person
  include Named
  name!
end

p = Person.new
p.name = "Trump"
p.describe #⇒ "Person is: Trump"

在你的例子中,它的意义是零。

© www.soinside.com 2019 - 2024. All rights reserved.