3

我现在正在阅读“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 吗?不是每个新属性都为类更改添加了另一个可能的原因吗?显然,答案不是将这些属性中的每一个都分成单独的类。那么,在哪里划界呢?

4

2 回答 2

2

您写了有关该OrderConverter课程的文章,但没有显示该课程的来源。我假设这个类包含方法 to_xml、to_json、to_csv 和 to_pdf。

将这些方法中的每一个分离成一个单独的转换器类(即 PdfConverter、CsvConverter、XmlConverter 等)不是更“正确”吗?

是的,将这些方法分离到转换器类可能是一个好主意:每个转换器类将只负责一种格式(一种责任)。

...如果一个模型具有多个属性(即,汽车有多个门和一种颜色)以及相应的 attr_accessors,这是否已经违反了 SRP?

不,这些属性(如颜色、门数……)不是一组职责!类的职责Car是描述汽车(保存关于汽车的信息)。汽车的每个实例将描述一辆车。例如,如果汽车是模型类(假设您想将汽车存储在 DB 中,并且汽车的一个实例是 DB 中的一行),那么如果您想更改描述汽车的方式,则必须更改 Car 类在您的系统中。

但是,如果汽车将定义例如生产者,并且将通过名称和地址来描述生产者,那么我会将生产者的详细信息提取到其他类,因为这是描述汽车生产者而不是汽车本身的责任.

还有一件事。SRP 不是一种模式。这是一个原则(SOLID 中的第一个)。这个术语是由罗伯特·塞西尔·马丁介绍的。在这里http://www.butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod您可以找到更多信息。

于 2013-10-23T22:54:20.480 回答
0

您似乎认为将门的颜色和数量提取到单独的类中太过分了,我不同意。事实上,如果汽车是一个非常简单的汽车类,也许它有点过头了。然而,只有一定数量的有效颜色,只有一定数量的门是有效的。如果我希望能够通过一系列别名中的任何一个来识别我的汽车颜色怎么办?

CarColor 对我来说似乎是一个完全有效的课程。它可以存储什么是有效颜色和无效颜色的所有逻辑,提供别名等。

考虑一个我最近处理过的一个例子,它涉及一系列不同形式的地方的名字:

数据库 1:琼·史密斯

对,我会取名字,把它粘在一个字符串里,放在我的人身上。没有必要大惊小怪地将一件事包装在它自己的班级中。

数据库 2:史密斯,琼

好的,所以我将覆盖设置器,检查这种格式并在正确时翻转它。

数据库 3:人:{ 头衔:夫人,名字:Joan,姓氏:Smith }

对,我们只需将标题放在开头,将最后两个连接在一起,然后将整个内容粘到我们该死的字符串中。

数据库 4:不符合您的结构的外来名称和首、中、尾的整个概念具有完全不同的含义。

...原来名字很复杂。

通过采用您所谓的简单结构并委托给另一个类来处理名称的含义、两个名称何时相同、我们如何打印它们等,这一切都可以很好地避免。

过早地将所有单例值包装在它们自己的类中可能有点矫枉过正 - 但我发现,在有疑问时倾向于包装往往会在以后很好地为我服务,即使当时感觉很愚蠢,尤其是当值渗透到其余部分时代码,因此稍后更改它会影响一切。

于 2013-10-24T00:23:49.770 回答