对 OOP 中单一职责的困惑

Confusion about single responsibility in OOP

让我们考虑以下示例:

class User
{

}

class FirstUseNotification
{
    function show(User user)
    {
        // check if it was already shown, return if so
        // show a notification
        // mark it as shown in the db or whatever
    }
}

class SomeController
{
    function someMethod()
    {
        firstUseNotification->show(user);
    }
}

show() 方法似乎通过做 3 件事打破了单一职责。所以我认为这可以这样重写:

class User
{

}

class FirstUseNotification
{
    function show(User user)
    {
        // show a notification
    }

    function shouldShow(User user)
    {
        // return true if not yet shown
    }

    function markAsShown(User user)
    {
        // flag notification as shown
    }
}

class SomeController
{
    function someMethod()
    {
        if (firstUseNotification->shouldShow(user)) 
        {
            firstUseNotification->show(user);
            firstUseNotification->markAsShown(user);
        }
    }
}

我感兴趣的是:

  1. 我假设在第二个示例中,通知 class 现在符合单一责任原则是否正确?
  2. show() 方法中发生的所有事情都消失了,但是......它们只是重新定位到控制器中的一个方法,所以这不应该意味着这个控制器方法现在打破了单一职责吗?如果是这样,如何才能做到合规?

单一责任原则 (SRP) 通常以 Robert C. Martin 的引述形式表述:

A class should have only one reason to change.

在这种情况下,您 FirstUseNotification class 的目的是向首次使用的用户显示通知。所以这个 class 需要改变的唯一原因是这个目的改变了;这是一个原因,所以SRP是满意的。

请注意,此定义适用于 class,不适用于方法。也就是说,将此方法拆分为三个方法可能 违反 SRP,因为如果此 class 的用户需要调用三个方法而不是一个,那么该用户 class 有责任检查是否显示通知,并将用户标记为所示, 除了 用户 class 自己的责任。 FirstUseNotification 的责任是 "show a notification to a first-time user",而不是提供一个 API 允许其他 class 在他们没有责任的情况下这样做。

实际上,FirstUserNotification class 可能有其他更改原因,如果它如何显示通知或访问数据库的详细信息发生更改。理想情况下,可以通过为通知和数据库 classes 提供稳定的接口来避免这种情况,因此对那些 classes 的更改不需要更改 FirstUseNotification 或其他 class显示通知 and/or 的 es 访问数据库。实际上,这并不总是 100% 实现,在这种情况下,FirstUseNotification class 可能有一些责任与显示通知或访问数据库的细节有关。但理论上,如果其他 class 正确处理了自己的职责,那么这个 class 只有一个改变的理由。