【问题标题】:What is your best example of a violation of the Single Responsibility Principle?您违反单一责任原则的最佳例子是什么?
【发布时间】:2009-08-30 18:57:01
【问题描述】:
我正在寻找一些违反单一职责原则的优秀代码示例。不要向我展示 Bob 叔叔的书籍或网站中的任何示例,因为这些示例遍布整个互联网,例如:
interface Modem
{
public void dial(String pno);
public void hangup();
public void send(char c);
public char recv();
}
【问题讨论】:
标签:
single-responsibility-principle
solid-principles
【解决方案1】:
您的 OO 设计的粒度是个人喜好问题,可能不适合其他人。因此,我不会在某些业务逻辑类中寻找打破单一责任原则的例子,讨论它是否有太多或太少的事情要做。
在我看来,最好的例子(副作用最严重)来自于打破应用程序的分层。例如:
- 在数据访问层执行业务逻辑(其唯一职责应该是提供对应用程序的持久访问)
- 从(通过)域模型(其唯一职责应该是存储应用程序的大部分状态)访问业务服务
- 在视图层执行复杂的业务逻辑(负责数据呈现和用户输入)
【解决方案2】:
实际上,在我使用过的大多数 OO 语言中,顶级的 Object 类就是一个很好的例子。例如,在 Ruby 中,Object 类(或者更准确地说是混入Object 的Kernel mixin)有45 个公共实例方法。现在,其中一些是化名,但至少还有 20 个,而且他们来自世界各地。我可以轻松确定至少 5 项职责。
现在,我并不是要选择 Ruby。它是我最喜欢的编程语言。这就是我用它作为例子的原因:因为它是我最熟悉的语言。而且我有理由相信,我写的关于 Ruby 的内容至少也 100% 适用于 Java 和 .NET。
【解决方案3】:
这是我必须承担的 PHP 项目的一些代码:
class Session
{
function startSession()
{
// send HTTP session cookies
}
function activateUserAccount()
{
// query the DB and toggle some flag in a column
}
function generateRandomId()
{}
function editAccount()
{
// issue some SQL UPDATE query to update an user account
}
function login()
{
// perform authentication logic
}
function checkAccessRights()
{
// read cookies, perform authorization
}
}
我相信这门课做得很好。
【解决方案4】:
关于 SRP 的线索是定义职责,以便您的实现只做那件事。这就像您正在制定规则(通过为类指定名称和责任)然后尝试遵循它。
因此,如果您不遵守它,您要么 没有正确定义规则,或者您在实施规则时不一致(或两者兼而有之,这实际上可能是最常见的情况)。
我通常发现那些在定义单一主要职责或良好名称方面没有做出半体面尝试的类是最好的违规行为。然后,您只需阅读整个课程,以确定是否有任何明确定义的职责。
【解决方案6】:
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
@interface Spreadsheet : NSObject
- (void)loadFromURL:(NSURL *)url;
- (void)saveToURL:(NSURL *)url;
- (void)drawTo:(CGRect*)targetArea withContext:(CGContextRef *)context;
@end
我认为上述示例违反了 SRP。
从表面上看,很明显该类负责一件事:电子表格。它有别于文档管理问题领域中的其他实体,例如文字处理。
但是,请考虑电子表格对象可能更改的原因。
电子表格的基础模型可能会发生变化。这会影响加载和保存代码,但不会影响电子表格的绘制方式。因此加载/保存职责与绘图职责是分开的。我们的班级有两个职责。
因此,如果我们考虑更改类的所有合理可预见的原因,并看到只有类上的特定方法会受到影响,我们就会看到一个机会来分解责任以获得更集中的类。
修改后的课程是:
@interface SpreadsheetEncoder
- (NSData *)encodedSpreadsheet:(Spreadsheet *)spreadsheet;
- (Spreadsheet *)spreadsheetFromEncodedData:(NSData *)data;
@end
@interface Spreadsheet2 : NSObject
- (NSData *)data;
- (instancetype)initSpreadsheetFromData:(NSData *)data;
- (void)drawTo:(CGRect*)targetArea withContext:(CGContextRef *)context;
@end
随着产品开发的推进,我们可以再次问自己“有什么可以改变”,然后重构类,让它们只对一个问题负责。 SRP 仅与问题域和我们在给定时间对它的理解有关。
在我看来,SRP 归结为询问“可以改变什么?”和“会受到什么影响”。当“什么可以改变”只映射到一件受影响的事情上时,你就有了实现 SRP 原则的类。