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

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); } },oop,solid-principles,single-responsibility-principle,Oop,Solid Principles,Single Responsibility Principle,show()方法似乎通过做3件事来打破单一责任。所以我想这可以改写成这样: class User { } class FirstUseNotification { function show(User user) { // show a notification } function shouldShow(User user) { // return true if not yet shown } f

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);
        }
    }
}
我感兴趣的是:

  • 我假设在第二个示例中,通知类现在符合单一责任原则,对吗
  • show()方法中发生的所有事情都消失了,但是。。。它们只是被重新定位到控制器中的一个方法,所以这难道不意味着这个控制器方法现在打破了单一的责任吗?如果是,如何做到这一点

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

    一个类应该只有一个更改的原因

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

    请注意,此定义适用于类,而不是方法。也就是说,将此方法拆分为三个方法可能违反了SRP,因为如果此类的用户需要调用三个方法而不是一个方法,那么除了用户类自己的责任外,该用户类还负责检查是否显示通知,并将用户标记为如图所示
    FirstUseNotification
    的责任是“向首次用户显示通知”,而不是提供允许其他类在其不负责时执行此操作的API


    实际上,
    FirstUserNotification
    类可能有其他原因需要更改,如果它显示通知或访问数据库的详细信息发生了更改。理想情况下,可以通过为通知和数据库类提供稳定的接口来防止这种情况,这样对这些类的更改就不需要更改
    FirstUseNotification
    或其他显示通知和/或访问数据库的类。在实践中,这并不总是100%实现的,在这种情况下,
    FirstUseNotification
    类可能会负责显示通知或访问数据库的细节。但从理论上讲,如果其他班级正确地处理自己的职责,那么这个班级只有一个理由需要改变。

    谢谢你的回答。然而,作为一个不相关的问题,在第一个例子中,是否有违反SRP以外的其他原则?我无法想象有一种方法会产生副作用(在本例中是向db写入)是一件好事。原则是“避免副作用”。这是函数式编程的原则,但在OOP等其他范式中也有好处(包括Java在内的大多数流行语言现在都是多范式的)。好处包括使代码更容易推理,并且更容易与其他代码隔离测试。要以当前设计的方式对
    FirstUserNotification
    进行单元测试,您可能必须模拟显示通知和数据库更新部分;另一方面,您可以重新设计它,以返回一个表示状态更改为。。。。。。这可能更容易测试,因为您可以测试它是否返回表示正确通知和状态更改的对象,而不是模拟协作者。另一方面,这可能会使它更难使用,因为您必须返回一个表示需要执行的多个副作用的复杂对象,并且您必须有另一个负责执行这些副作用的类。一般来说,对于OOP,我建议在方便的地方避免副作用,但您并不总是需要。您可能还对与此相关的设计模式感兴趣。