【发布时间】:2023-03-23 05:05:01
【问题描述】:
如果使用观察者设计模式的应用程序具有具有以下职责的subject 类:
1) 管理和通知观察者(即提供注册和注销函数,调用所有观察者的通知函数)和
2) 其最初的职责(即在成为subject 之前,该类在做什么)。
这个类是否违反了单一职责原则?它显然有不止一个责任,但阅读 SRP 我很困惑“改变的原因”是在设计时还是在运行时改变?
【问题讨论】:
标签: design-patterns
如果使用观察者设计模式的应用程序具有具有以下职责的subject 类:
1) 管理和通知观察者(即提供注册和注销函数,调用所有观察者的通知函数)和
2) 其最初的职责(即在成为subject 之前,该类在做什么)。
这个类是否违反了单一职责原则?它显然有不止一个责任,但阅读 SRP 我很困惑“改变的原因”是在设计时还是在运行时改变?
【问题讨论】:
标签: design-patterns
不,Observer 设计模式不违反Single Responsibility Principle (SRP)。
什么是责任?
“责任表示对象有义务提供 某种行为。”
[面向对象的分析和设计,Grady Booch [等人],第 600 页]
但是 SRP 对责任的定义不同,作为改变的理由。 SRP 声明一个类应该只有一个职责 (改变的原因)。
这符合GoF原则 封装变化的事物 - 许多 GoF 设计模式的主题。 例如,策略模式封装了一个算法 (可以更改)在单独的 Strategy 类中。
观察者模式并不是要封装变化的事物。 它描述了一种在交互对象之间定义一对多依赖而不使对象紧密耦合的方法。
如需进一步讨论,请参阅 GoF 设计模式记忆学习 面向对象设计与编程 http://w3sdesign.com.
【讨论】:
在我看来:是的,观察者模式确实违反了 SRP(有时)。
想象以下类,它是MVC 应用程序的一部分(Java 伪语法):
class Model {
void setDateOfBirth(Date);
Date getDateOfBirth();
int getAge(); // calculated from date of birth
void registerObserver(Observer);
void unregisterObserver(Observer);
void notifyObservers();
}
前三种方法显然与管理应用程序数据有关,而后三种方法则没有。
SRP 指出,一个类应该只有一个改变的理由[1]。我可以想到多种原因来更改不应影响应用程序数据管理的观察者方法。一些例子:
(有人可能会说,所有这些东西都可以隐藏在 Observer 本身中。但是注册一个允许同时注册其他观察者的观察者似乎也不对,IMO。)
顺便说一句,观察者模式也违反了DRY (don't repeat yourself) principle,因为你需要一遍又一遍地实现注册、注销和通知机制(即所有观察者的循环)。
但是:
这两个职责应该分开吗?这取决于应用程序的变化方式。 […] 如果 […] 应用程序没有以导致两种职责在不同时间发生变化的方式发生变化,那么就没有必要将它们分开。事实上,将它们分开会散发出不必要的复杂性。 — [1]
因此,您必须自己决定并平衡 SRP 和 DRY 与不必要的复杂性。
[1] The Principles of OOD Robert C. Martin(又名 Uncle Bob)和他的书“敏捷软件开发、原则、模式和实践”,第 8 章。
【讨论】:
回答我自己的问题,因为尽管 bav 的答案链接到一个很好的资源,但它并没有回答这个问题。 (虽然看视频给了我理解,让我可以回答这个问题!)
不,这是对单一职责原则 (SRP) 的误解。
SRP 中的责任是指客户角色,他可以发起变更请求。
例如,如果类Employee 具有calculatePay() 和displayEmployee() 方法,则可以合理地假设它违反了SRP,因为calculatePay 将“属于”公司会计师,而displayEmployee 将属于报告文员。这意味着两个具有不同角色的人可以请求更改一个类。
观察者模式不会添加新职责(至少不是 SRP 职责),因为没有客户角色会关心此类将更改发布给其观察者。
【讨论】:
是的。 但是……
让我们回到设计模式背后的大理念, 这基本上可以帮助开发人员更轻松地处理未来未决的更改。
为您的设计设置的粒度级别由您决定,通往黄金中间的路径应以您的常识、经验和应用程序的规模为指导。
如果提前看到对您的存储库分发的更改,您可以创建一个中间件 (pub-sub),负责将您的更改分发给观察者。
【讨论】: