我花了一些时间再次阅读Rails Antipatterns。在页81,有一个示例将属性非规范化为模型上的文本字段。我会简化这个例子;请假设state是文章表中的一列:
class Article < ActiveRecord::Base
STATES = %w(review published)
validates :state, :inclusion => {:in => STATES}
STATES.each do |state|
define_method "#{state}?" do
self.state == state
end
end
class << self
STATES.each do |state|
define_method "#{state}" do
state
end
end
end
end
我试图解决的问题是在类&lt;&lt;类中定义的方法。自我阻止。当你说class&lt;&lt;自我并在那个黑色中定义方法,因为self是类文章,我正在定义文章的类方法,对吧? Article.review,Article.published。我只是看不出目的是什么。如果目的是设置一些访问器,为什么不在第一个“#{state}?”的上部循环中定义那些访问器。方法已定义。
这有什么问题吗?我在书上找了勘误,没找到任何东西。
答案 0 :(得分:3)
本书中的例子是这个反模式的重构:
@article.state = State.find_by_name("published")
到此:
@article.state = State.published
然后到此:
@article.state = Article.published
最后一个是暗示的,未在书中显示。
正如你所注意到的那样,制作一个只返回自己名字的类方法似乎没什么意义,所以这个例子的真正意义并不在于这是做事的最佳方式,而是作为书。陈述:
最重要的是它没有代表的复杂性和代码。
即。动态创建的方法曾经在State
类中,而且这个类是不必要的,可以删除。
然而,这样做还有另一个好处。定义方法的目的是使用标准化接口为DB列创建一组常量。最初,常量的名称将与访问器方法的名称相同,但将来可能有理由进行更改。在这种情况下,可以调用这些访问器方法的程序的其他区域可以保持不变。
将此与在整个地方硬编码常量的实际字符串值的替代方法进行对比。在这种情况下,Article
的数据库结构的知识泄漏并创建依赖关系,因此对Article
类内部(DB结构)的更改将会波及其他具有不可预测影响的区域。通过使用消息而不是常量的实际值,这些依赖关系将被删除,并且如果需要重构,任何重构都将是微不足道的。您可以轻松地将任何这些动态方法重新定义为更复杂的东西,而不会导致Article
之外的任何其他内容需要更改。
换句话说:为了正确封装数据,良好的OO设计要求知道数据库列的名称不应该离开Article
类。你应该总是更喜欢方法而不是变量,以使这成为可能,这就是那个类方法的作用。
答案 1 :(得分:0)
第二个循环是在Article
上定义类方法,它返回每个状态的字符串表示。那是Article.review
返回"review"
。
答案 2 :(得分:0)
在红宝石中,那两个下一个区块是相同的
class MyClass
class << self
def my_method
puts 'this is a method'
end
end
end
和这个块
class MyClass
def self.my_method
puts 'this is a method'
end
end
您只是避免为您定义的每个类方法键入self
。
您可以参考此blog post获取更详细的解释