在“现实世界”应用单一职责原则

19
我想了解在实际编写代码中认为使用单一职责原则是合理的人群所占比例,以及真正实践该原则的人数。Joel在 Podcast#38 中谈到这个OOP原则在现实世界中是多么无用;而且进一步表明像Uncle Bob这样的人可能没有编写过非常复杂的系统。

我个人参与了几个软件项目的编写或扮演了重要角色,但直到最近我才接触到这种设计模式。我很喜欢这个原则,并且真的想开始使用它。我发现Joel在播客中的论点相当薄弱(如果您继续阅读 这里的博客评论,其他人也是这样认为)。 但是,这是否有任何道理呢?

社区有什么看法?


我认为你应该把这个变成一个维基,因为没有一个单一明确的答案。 - user1228
我不确定我同意。我认为有更多的正确答案和更少的正确答案。我认为太多人认为这比实际情况更加主观或依赖于上下文,这也是我发布这个问题的部分原因。感谢您的评论。 - Thiru
9个回答

25
我有一些应用SOLID原则的经验,这些经验主要是好的。我也听过播客,似乎Jeff和Joel都没有尝试过他们谈论的东西足够长的时间来真正评估其优点。主要反对的论点通常是“你需要编写更多的代码”。如果我看看自己的工作,我可能会写10到20%的代码(通常是接口定义),但由于一切高度解耦合,因此更易于维护。我很少遇到一个部分的应用程序的更改会破坏其他部分的情况。因此,我必须维护的额外20%的代码可以为自己付款。
Jeff在代码质量方面也误解了重点。他认为代码质量对客户来说并不是一个巨大的益处。他是正确的,客户并不关心。客户确实关心快速实现新功能,这就是代码质量的作用所在。我发现,将代码质量保持尽可能高的投资总是在几个月内回报的。高质量=低维护成本。
我同意他们的观点,就像任何事情一样,您必须在这些事情上保持务实。如果您需要交付某些内容,那么请快速而脏地完成它。但之后清理它。

8
我正在开发一个项目,它能够完成许多不同的、非常复杂的任务,并且使用的框架应该易于被其他人扩展。
一开始,类都很大并且做了很多事情。为了改变这些行为,你必须扩展这些类。方法都是虚拟的,不改变对象的状态,所以这样做相当容易。
但随着系统的增长,很明显框架将会有一系列的巨大对象,每个对象都有长长的继承链。这也导致了意想不到的依赖关系 - 一个抽象方法需要使用X类的集合来生成Y对象,这在基类中定义,导致每个人都必须按照这种方式操作,即使对于继承树的一半而言,这种方式也没有任何意义。这也导致了庞大的类,需要进行数十个单元测试才能覆盖80%以上的代码,而且复杂性非常高,你无法确定是否正确地覆盖了所有部分。很明显,这种设计会导致框架非常僵硬和不灵活。
因此,我们沿着SRP的思路重新设计了所有东西。你会有你的接口、一个基类,可能还有一个或多个实现类。每个类都由不同的对象组成,这些对象执行整个过程的关键功能。如果你想改变其中的一部分,你不需要覆盖一个方法,你可以创建另一个对象来扩展必要的接口/基类,并以不同的方式执行它的工作。SRP甚至适用于类方法的参数和返回值。对于那些需要灵活性的系统部分,不是传递X类的集合来生成Y对象,而是创建一个类来封装Y对象的生成过程。系统中的组件将围绕这些生成器进行操作,与其他生成器组合(生成器的责任),最终使用它们来生成Y。这允许创建不同类型的生成器,所有这些生成器都可以被完全相同地处理,即使它们做了非常不同的事情。此举还大大减少了每个类的代码库,并使它们更容易测试。
我认为,对于新开发人员来说,要将所有内容分解到这个级别是非常困难的。你几乎必须写一个大块的代码,理解它的工作原理,然后将其重新设计为若干不同的组件,每个组件负责整体的一部分。

5
一些编程理论的最大问题在于,它们强调的是代码中好的属性实际上是好的代码属性。尽管这些理论本质上是正确的,但它们并不特别有用。
是的,代码应该写得好,是的,事情不应该被重复,是的,更改不应该在意想不到的地方导致奇怪的错误。但是,归根结底,表达解决方案的简单易懂、真正简单一致的代码比满足某些严格原则的复杂系统更有价值。作为一个行业,我们往往会把好的想法推向极端,为了追求“正确”的解决方案而创造出大量不必要的复杂性,而不是最好的解决方案。
保罗。

4

我没有阅读或听过Joel的评论,因此无法具体评论。

我认为你必须从目标的角度来看待单一职责原则,而不是严格要求。与软件开发中的许多事情一样,有理想的目标应该努力实现,但除非你的代码没有经济效益或成本,否则你必须在满足客户需求方面具有实用性。


我怀疑任何客户都不关心开发者的纪律和面向对象原则的执行,但是你(或其他任何人)必须维护/使用代码,可能会感激它。 - Jim Anderson
我并不反对。只是有时候实用主义胜过严格执行这个原则。 - BlackWasp

4
在实践中,在面向对象的情况下,很难获得真正的单一职责原则。考虑一个存在于复杂系统中的类。它可能只有一个业务相关的职责(例如打印报告),但它将在内部具有许多其他职责,这些职责违反了纯单一职责原则,例如日志记录和异常处理。如果您显着更改日志记录机制,则可能需要更改打印类进行的调用。

这就是AOP的产生原因。使用它,您无需更改任何内容,只需更改日志记录类即可更改日志记录。

为了明显的原因,追求面向业务的单一职责原则是好的,但对于足够复杂的系统,您永远无法获得真正的OOP单一职责原则。AOP可以,但OOP不行。

我不知道Joel的推理是否如此,但这就是我处理该想法的方式。


1

我认为SOLID原则有时不符合设计类时通常应用的自然/业务逻辑。Robert C. Martin提出了一个关于矩形和正方形类的例子(正方形不应该是矩形的子类)。

http://www.hanselminutes.com/default.aspx?showID=163

我不确定它是否涉及SRP,但是思想是相同的。SOLID原则可能会导致违反直觉的决策,很难克服这个障碍。

对我来说,“指南”比“原则”更合适的名称。


1

我认为作为一个遵循SRP的有纪律的OO开发者所获得的最大好处是易于测试和维护,因此我认为SRP是任何需要被测试/维护的系统(基本上除了一些临时系统)的一个良好目标。


0

我一般同意SOLID原则,但你也得考虑到它们的背景。如果你正在编写一个概念验证(POC),那么SOLID原则的适用性会降低。

另一方面,如果你正在开发一个寿命长达数年的产品,那么你最好仔细研究并应用SOLID原则。否则公司将花费大量资金来提高生产力。

关于Joel和他的评论,他有一个有效的观点。有时候产品需要发布,否则公司就会失败。这就是现实。

作为开发人员,你的工作是发布产品,但紧迫的截止日期并不意味着你要选择一个差劲的架构。例如,你使用数据集还是业务实体的选择是一个简单的决定,两者在实现方面都有类似的付出,但长期来看,两者在新功能开发和维护方面的差异是显著的。


0

我认为编写一个简单的一行代码作为所有类的主要职责应该是可能的。这个一行代码中的“and”通常不是一个好兆头。然后,单一职责往往归结为选择适当的抽象。

GUI附近的类往往反映了GUI并具有“作为XXXX的GUI”的单一职责。Coward的解决方案..


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接