我现在正在阅读“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类仍有多个原因需要改变:
将这些方法中的每一个分成单独的转换器类(即PdfConverter,CsvConverter,XmlConverter等)会不会更“正确”?这样,每个转换器类改变的唯一原因是转换方法本身是否改变。如果需要新格式,则可以创建新类。如果不再需要旧格式,则可以简单地删除该类。原始的Order模型是否真的应该负责查找自己的实例?难道你不能将'find'方法分成单独的'OrderFinder'类吗?
我已经阅读了Sandi Metz的“Ruby实用面向对象设计”的SRP章节,她建议进行以下测试,以确定一个班级是否只有一个责任:
如何确定Gear类是否包含属于某处的行为 其他?一种方法是假装它是有感知的并且询问它。如果你 将每一个方法都重新描述为一个问题,提出问题 感。例如,“请先生齿轮,你的比例是多少?”看起来很完美 合理,而“请先生齿轮,你的装备是什么?”是不稳定的 地面,和“请先生齿轮,你的轮胎(尺寸)是多少?”真是太彻头彻尾了 可笑了。
但是如果一个模型具有多个属性(即一辆车有一个门和一个颜色)和相应的attr_accessors,那么这是不是已经极端了,这是不是已经违反了SRP?每个新属性是否都会为类更改添加另一个可能的原因?显然,答案不是将这些属性中的每一个分成不同的类。那么人们在哪里画线?
答案 0 :(得分:2)
你写的是OrderConverter
类,但是你没有显示该类的来源。我假设这个类包含方法to_xml,to_json,to_csv和to_pdf。
将这些方法中的每一个分成单独的转换器类(即PdfConverter,CsvConverter,XmlConverter等)会不会更“正确”?
是的,将这些方法分离到转换器类是个好主意:每个转换器类只对一种格式(一种责任)负责。
...如果一个模型有多个属性(即一辆车有一个门和一个颜色)和相应的attr_accessors,这不是已经违反了SRP吗?
不,这些属性(如颜色,没有门,......)不是一套责任! Car
课程的责任是描述一辆汽车(持有汽车的信息)。汽车的每个实例都将描述一辆汽车。例如,如果一辆汽车是一个模型类(比方说你想把汽车存放在数据库中,而汽车的一个实例是DB中的一行)那么你必须改变汽车类,如果你想改变一个方式在您的系统中描述汽车。
但是如果汽车将定义为例如生产者,并且生产者将通过名称和地址来描述,那么我将生产者的细节提取到其他类,因为这是描述生产者的责任。汽车而不是汽车本身。
还有一件事。 SRP不是一种模式。这是一个原则(首先在SOLID中)。这个词由Robert Cecil Martin介绍。在http://www.butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod,您可以找到更多信息。
答案 1 :(得分:0)
你似乎决定将门的颜色和数量提取到单独的类别太远了,我不同意这一点。事实上,如果汽车是一个非常简单的汽车类,也许它有点超过顶部。但是,只有一定数量的有效颜色,只有一定数量的门是有效的。如果我想通过一系列别名识别我的汽车颜色怎么办?
CarColor对我来说似乎是一个非常有效的课程。它可以存储有效颜色的所有逻辑,提供别名等。
考虑一个我最近处理的关于不同形式的一系列地名的例子:
数据库1:Joan Smith
是的,我会取名字,把它贴在一个字符串中并把它放在我的身上。毫无疑问,在自己的课堂上做一件大惊小怪的事情。
数据库2:史密斯,琼?
好的,所以我会覆盖这个设置器,检查这个格式并翻转它是否正确。
数据库3:person:{title:Mrs,first:Joan,last:Smith}
是的,我们只是将标题粘贴在开头,将最后两个连接在一起,然后将整个内容粘贴到我们该死的字符串中。
数据库4:不遵循你的结构的外国名称,第一,中,最后的整个概念具有完全不同的含义。
......事实证明,名字很复杂。
通过采用所谓的简单结构并委托给另一个类来处理名称的含义,两个名称何时相同,我们如何打印它们等等,这一切都可以很好地避免。
过早地将所有单身人士的价值包装在他们自己的班级中可能有些过分 - 但我发现,如果有疑问,那么即使在当时感觉很愚蠢时,对于包装有偏见也会对我有利,特别是当价值渗透时通过其余的代码,以后更改它会影响一切。