违反单一责任原则的最佳例子是什么?

发布于 2024-08-03 07:16:19 字数 232 浏览 11 评论 0原文

我正在寻找一些违反单一职责原则的良好代码示例。不要向我展示鲍勃叔叔的书籍或网站中的任何示例,因为这些示例在互联网上随处可见,例如:

interface Modem
{
    public void dial(String pno);
    public void hangup();
    public void send(char c);
    public char recv();
}

I'm looking for some good examples of code that violates the Single Responsibility Principle. Don't show me any examples from Uncle Bob's books or web sites since those are plastered all over the internet, like this one:

interface Modem
{
    public void dial(String pno);
    public void hangup();
    public void send(char c);
    public char recv();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

月依秋水 2024-08-10 07:16:19

面向对象设计的粒度取决于个人喜好,可能不适合其他人。因此,我不会在某些业务逻辑类中寻找打破单一责任原则的例子,讨论它是否有太多或太少的事情要做。

在我看来,最好的例子(具有最严重的副作用)来自于打破应用程序的分层。 F.ex.:

  • 在数据访问层中执行业务逻辑(其唯一的职责应该是提供对应用程序的持久访问)
  • 从(通过)域模型访问业务服务(其唯一的职责应该是存储大部分应用程序的状态)
  • 在视图层执行复杂的业务逻辑(负责数据呈现和用户输入)

The granularity of your OO design is a matter of taste and might be inapropriate for others. Thus, I wouldn't look for examples of breaking the single responsibility principle in some business-logic-class, discussing whether it has too much or too little to do.

In my opinion, the best examples (with worst side-effects) come from breaking the layering of your application. F.ex.:

  • Performing business logic in the data access layer (whose only responsibility should be providing persistence access to the application)
  • Accessing business services from (through) the domain model (whose only responsibility should be to store most of the application's state)
  • Performing complex business logic in the view layer (responsible for data presentation & user input)
み青杉依旧 2024-08-10 07:16:19

以下是我必须承担的 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
    }
}

我相信这个类确实做了很多事情。

Here's some code from a PHP project I had to take on:

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
    }
}

I believe this class does waaay to much.

知你几分 2024-08-10 07:16:19

关于 SRP 的线索是定义职责,以便您的实现能够完成该任务。这就像您正在制定规则(通过为类指定名称和职责)然后尝试遵循它。

因此,如果您不遵循它,那么您要么没有正确定义规则,要么在实现规则时不一致(或两者兼而有之,这实际上可能是最常见的情况)。

我通常发现那些在定义单一主要责任或好名字方面没有做出半体面尝试的类是最好的违规行为。然后你只需要阅读整个课程来尝试确定是否有任何明确定义的职责。

The clue about SRP is to define the responsibilities so that your implementation does just that thing. It's like you're making a rule (by giving a class a name and a responsibility) and then trying to follow it.

So if you're not following it you're either not defining the rule properly or you're being inconsistent while implementing it (or both, which might actually be the most common case).

I generally find the classes that do not give a half-decent try at defining a single main responsibility or good name to be the best violations. Then you'll just have to read the whole class to try to determine if there's any well defined responsibilities at all.

于我来说 2024-08-10 07:16:19

实际上,在我使用过的大多数面向对象语言中,顶级 Object 类就是一个很好的例子。例如,在 Ruby 中,Object 类(或更准确地说,Kernel mixin,它被混合到 Object 中)有 45 个公共实例方法。现在,其中一些是化名,但仍然至少有 20 个,而且他们来自世界各地。我可以轻松识别至少 5 项职责。

现在,我并不是要挑衅 Ruby。这是我最喜欢的编程语言。这就是为什么我用它作为例子:因为它是我最熟悉的语言。我有理由确信我所写的有关 Ruby 的内容至少也 100% 适用于 Java 和 .NET。

Actually, in most OO languages that I have used, the top-level Object class is a good example. In Ruby, for example, the Object class (or more precisely the Kernel mixin, which gets mixed into Object) has 45 public instance methods. Now, some of those are aliases, but there's still got to be at least 20, and they are from all over the place. I can easily identify at least 5 responsibilities.

Now, I don't mean to pick on Ruby. It is my favorite programming language. That's why I used it as an example: because it's the language I'm most familiar with. And I am reasonably sure that what I wrote about Ruby applies 100% also to at least Java and .NET.

烟花肆意 2024-08-10 07:16:19

确定类的“责任”是一个定性问题。
仅查看给定的类代码,绝不能让我们了解它处理其职责的情况。
至少根据我的经验,非常有必要考虑对类的需求更改将如何传播到其他模块(或者其他类的更改将如何传播到此类)。

正如@schmeedy对“打破系统的分层”给出了很好的解释,我认为这只是分析“责任域”的方法之一。

我试图进一步展开讨论。我的尝试是以定量的方式定义“责任”。
我博客上的一些讨论: http://design-principle -pattern.blogspot.in/2013/12/single-responsibility-principle.html

It's a qualitative question to ascertain the 'responsibilities' of a class.
Just looking at a given class code, can in no measure give us an idea of how well it handles it's repsonsibility.
It is very necessary, atleast as per my experience, to take into account how the requirement changes to the class will propagate to other modules ( or how will the changes from other classes get propagated to this class).

As @schmeedy gives a nice explanation of 'breaking the layering of the system' , which I think is just one of the ways to analyse 'responsibility domain'.

I have attempted to take the discussion little further. My attempts are to define 'responsibility' in a quantitative way.
Some discussions at my blog: http://design-principle-pattern.blogspot.in/2013/12/single-responsibility-principle.html

七秒鱼° 2024-08-10 07:16:19
#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 原则的类。

#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

I would argue that the above example violates the SRP.

On the face of it, it is clear that the class is responsible for one thing: Spreadsheets. It is distinguished from other entities in the Document Management problem domain such as Word Processing.

However, consider the reasons why the Spreadsheet object could change.

There may be a change to the model underlying the Spreadsheet. This affects load and save code but would not affect how the Spreadsheet is drawn. So the load/save responsibilities are separate from the drawing responsibilities. Our class has two responsibilities.

So if we think about all the reasonably foreseeable reasons to change a class, and see that only particular methods on the class would be affected, we see an opportunity to factor out a responsibility to get a more focussed class.

A revised class would be:

@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

As product development advances, we can ask ourselves again 'what could change' and then refactor the classes to keep them responsible for only one concern. SRP is only relative to the problem domain and our understanding of it at a given time.

SRP in my view boils down to asking 'what can change?' and 'what would be affected'. When 'what can change' maps onto only one thing that is affected, you have classes that implement the SRP principle.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文