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件事来打破单一责任。所以我想这可以改写成这样: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
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);
}
}
}
我感兴趣的是:
单一责任原则(SRP)通常以Robert C.Martin引用的形式陈述: 一个类应该只有一个更改的原因 在这种情况下,
FirstUseNotification
类的目的是向首次用户显示通知。所以这个类需要改变的唯一原因是如果这个目的改变了;这是一个原因,因此SRP是令人满意的
请注意,此定义适用于类,而不是方法。也就是说,将此方法拆分为三个方法可能违反了SRP,因为如果此类的用户需要调用三个方法而不是一个方法,那么除了用户类自己的责任外,该用户类还负责检查是否显示通知,并将用户标记为如图所示FirstUseNotification
的责任是“向首次用户显示通知”,而不是提供允许其他类在其不负责时执行此操作的API
实际上,
FirstUserNotification
类可能有其他原因需要更改,如果它显示通知或访问数据库的详细信息发生了更改。理想情况下,可以通过为通知和数据库类提供稳定的接口来防止这种情况,这样对这些类的更改就不需要更改FirstUseNotification
或其他显示通知和/或访问数据库的类。在实践中,这并不总是100%实现的,在这种情况下,FirstUseNotification
类可能会负责显示通知或访问数据库的细节。但从理论上讲,如果其他班级正确地处理自己的职责,那么这个班级只有一个理由需要改变。谢谢你的回答。然而,作为一个不相关的问题,在第一个例子中,是否有违反SRP以外的其他原则?我无法想象有一种方法会产生副作用(在本例中是向db写入)是一件好事。原则是“避免副作用”。这是函数式编程的原则,但在OOP等其他范式中也有好处(包括Java在内的大多数流行语言现在都是多范式的)。好处包括使代码更容易推理,并且更容易与其他代码隔离测试。要以当前设计的方式对FirstUserNotification
进行单元测试,您可能必须模拟显示通知和数据库更新部分;另一方面,您可以重新设计它,以返回一个表示状态更改为。。。。。。这可能更容易测试,因为您可以测试它是否返回表示正确通知和状态更改的对象,而不是模拟协作者。另一方面,这可能会使它更难使用,因为您必须返回一个表示需要执行的多个副作用的复杂对象,并且您必须有另一个负责执行这些副作用的类。一般来说,对于OOP,我建议在方便的地方避免副作用,但您并不总是需要。您可能还对与此相关的设计模式感兴趣。