我现在正在阅读“Rails AntiPatterns”,其中提到的第一个模式是单一职责原则。在这一点上,我已经遇到了足够多的 SRP 来意识到它是一个基本概念,对于像我这样的初学者来说是可以理解的,这就是为什么它如此令人沮丧以至于它还没有点击的原因。
这本书给出了一个 Order 类的例子:
class Order < ActiveRecord::Base
def self.find_purchase
#...
end
def self.find_waiting_For_review
#...
end
def self.find_waiting_for_sign_off
#...
end
def self.advanced_search(fields, option = {})
#...
end
def self.simple_search
#...
end
def self.advanced_search
#...
end
def to_xml
#...
end
def to_json
#...
end
def to_csv
#...
end
def to_pdf
#...
end
end
为了说明 SRP,本书建议将 4 个实例方法提取到单独的 OrderConverter 类中。这对我来说很有意义。但同时,这个 OrderConverter 类仍然可能有多个改变的原因:
- 如果应用程序不再需要上述 4 种格式之一,则必须删除相应的方法。
- 如果应用程序需要转换为其他格式,则需要实现更多方法。
- 如果用于将 Order 实例转换为不同格式的方法发生更改(假设它们都使用相同的方法,但参数不同,对应于所需的格式)。
将这些方法中的每一个分离成一个单独的转换器类(即 PdfConverter、CsvConverter、XmlConverter 等)不是更“正确”吗?这样,每个转换器类更改的唯一原因是转换方法本身是否更改。如果需要新格式,可以创建一个新类。如果不再需要旧格式,则可以简单地删除该类。原来的 Order 模型真的应该负责寻找自身的实例吗?您不能将“查找”方法分成单独的“订单查找器”类吗?
我已阅读 Sandi Metz 的“Practical Object-Oriented Design In Ruby”的 SRP 章节,她建议进行以下测试以查看一个类是否具有单一职责:
如何确定 Gear 类是否包含属于其他地方的行为?一种方法是假装它有知觉并审问它。如果您将其每一种方法都重新表述为一个问题,那么提出这个问题是有意义的。例如,“请问 Gear 先生,您的比例是多少?” 似乎完全合理,而“请问 Gear 先生,您的 gear_inches 是多少?” 在摇摇欲坠的地面上,“请问Gear先生,您的轮胎(尺寸)是多少?” 简直太荒谬了。
但极端一点,如果一个模型有多个属性(即一辆车有# of door 和一种颜色)和相应的attr_accessors,这不是已经违反了SRP 吗?不是每个新属性都为类更改添加了另一个可能的原因吗?显然,答案不是将这些属性中的每一个都分成单独的类。那么,在哪里划界呢?