大话设计模式笔记(十一)の观察者模式
举个栗子
问题描述
几个同事上班期间看股市行情,让前台MM帮忙看着老板什么时候过来查岗,老板进门的时候MM就拨电话给其中一个同事,于是所有同事都知道了,再继续工作。。。
简单实现
前台秘书MM
1. /**
2. * 前台秘书MM
3. * Created by callmeDevil on 2019/7/27.
4. */
5. public class Secretary {
6.
7. // 同事列表
8. private List<StockObserver> observers = new ArrayList<>();
9. private String action;
10.
11. // 增加
12. public void attach(StockObserver observer){
13. // 有几个同事请前台帮忙,于是就给集合增加几个对象
14. observers.add(observer);
15. }
16.
17. // 通知
18. public void call(){
19. // 待老板来了,就给所有登记的同事们发通知
20. for (StockObserver observer : observers) {
21. observer.update();
22. }
23. }
24.
25. public String getAction() {
26. return action;
27. }
28.
29. public void setAction(String action) {
30. this.action = action;
31. }
32.
33. }
34.
看股票同事
1. /**
2. * 看股票同事
3. * Created by callmeDevil on 2019/7/27.
4. */
5. public class StockObserver {
6.
7. private String name;
8. private Secretary sub;
9.
10. public StockObserver(String name, Secretary sub){
11. this.name = name;
12. this.sub = sub;
13. }
14.
15. public void update(){
16. System.out.println(String.format("%s %s 关闭股票行情,继续工作!", sub.getAction(), name));
17. }
18.
19. }
20.
测试
1. public class Test {
2.
3. public static void main(String[] args) {
4. // 前台妹子
5. Secretary mm = new Secretary();
6. // 看股票的同事
7. StockObserver observer1 = new StockObserver("哪路托", mm);
8. StockObserver observer2 = new StockObserver("啥是gay", mm);
9.
10. // 前台妹子记下两位同事
11. mm.attach(observer1);
12. mm.attach(observer2);
13. // 发现老板
14. mm.setAction("老板回来了!");
15. // 通知两个同事
16. mm.call();
17. }
18.
19. }
20.
测试结果
1. 老板回来了! 哪路托 关闭股票行情,继续工作!
2. 老板回来了! 啥是gay 关闭股票行情,继续工作!
3.
存在问题
- “前台MM”和“看股票同事”互相耦合
- 如果还有人想看NBA直播,那只能改动“前台MM”,不符合开放-封闭原则
- 其次应该遵循依赖倒转原则,让两者之间不相互依赖
简单实现2
抽象观察者
1. /**
2. * 抽象观察者
3. * Created by callmeDevil on 2019/7/27.
4. */
5. public abstract class Observer {
6.
7. protected String name;
8. protected Secretary sub;
9.
10. public Observer(String name, Secretary sub) {
11. this.name = name;
12. this.sub = sub;
13. }
14.
15. public abstract void update();
16.
17. }
18.
前台秘书MM
1. /**
2. * 前台秘书MM
3. * Created by callmeDevil on 2019/7/27.
4. */
5. public class Secretary {
6.
7. // 同事列表
8. private List<Observer> observers = new ArrayList<>();
9. private String action;
10.
11. // 增加
12. public void attach(Observer observer) { //针对抽象编程,减少了与具体类的耦合
13. observers.add(observer);
14. }
15.
16. // 减少
17. public void detach(Observer observer) { //针对抽象编程,减少了与具体类的耦合
18. observers.remove(observer);
19. }
20.
21. // 通知
22. public void call() {
23. for (Observer observer : observers) {
24. observer.update();
25. }
26. }
27.
28. public String getAction() {
29. return action;
30. }
31.
32. public void setAction(String action) {
33. this.action = action;
34. }
35.
36. }
37.
看股票的同事
1. /**
2. * 看股票的同事
3. * Created by callmeDevil on 2019/7/27.
4. */
5. public class StockObserver extends Observer {
6.
7. public StockObserver(String name, Secretary sub) {
8. super(name, sub);
9. }
10.
11. @Override
12. public void update(){
13. System.out.println(String.format("%s %s 关闭股票行情,继续工作!", sub.getAction(), name));
14. }
15.
16. }
17.
看NBA的同事
1. /**
2. * 看 NBA 的同事
3. * Created by callmeDevil on 2019/7/27.
4. */
5. public class NBAObserver extends Observer {
6.
7. public NBAObserver(String name, Secretary sub) {
8. super(name, sub);
9. }
10.
11. @Override
12. public void update() {
13. System.out.println(String.format("%s %s 关闭NBA 直播,继续工作!", sub.getAction(), name));
14. }
15.
16. }
17.
测试代码同上
存在问题
其实“前台MM”也应该抽象出来,如果老板回来时,MM来不及电话了,于是通知大家的任务变成谁来做?是的,这时候是老板本人变成了通知者。
观察者模式
定义
又叫做发布-订阅模式。定义了一种一对多的依赖关系,让多个观察者对象同事监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
UML图
代码实现
通知者接口
1. /**
2. * 通知者接口
3. * Created by callmeDevil on 2019/7/27.
4. */
5. public interface Subject {
6.
7. void attach(Observer observer);
8. void detach(Observer observer);
9. void call();
10.
11. String getAction();
12. void setAction(String action);
13.
14. }
15.
老板
1. /**
2. * 老板
3. * Created by callmeDevil on 2019/7/27.
4. */
5. public class Boss implements Subject {
6.
7. // 同事列表
8. private List<Observer> observers = new ArrayList<>();
9. private String action;
10.
11. @Override
12. public void attach(Observer observer) {
13. observers.add(observer);
14. }
15.
16. @Override
17. public void detach(Observer observer) {
18. observers.remove(observer);
19. }
20.
21. @Override
22. public void call() {
23. for (Observer observer : observers) {
24. observer.update();
25. }
26. }
27.
28. public String getAction() {
29. return action;
30. }
31.
32. public void setAction(String action) {
33. this.action = action;
34. }
35.
36. }
37.
抽象观察者
1. /**
2. * 抽象观察者
3. * Created by callmeDevil on 2019/7/27.
4. */
5. public abstract class Observer {
6.
7. protected String name;
8. protected Subject sub;
9.
10. public Observer(String name, Subject sub) { // 原来是“前台MM”,现改成“抽象通知者”
11. this.name = name;
12. this.sub = sub;
13. }
14.
15. public abstract void update();
16.
17. }
18.
看股票的同事
1. /**
2. * 看股票的同事
3. * Created by callmeDevil on 2019/7/27.
4. */
5. public class StockObserver extends Observer {
6.
7. public StockObserver(String name, Subject sub) { // 原来是“前台MM”,现改成“抽象通知者”
8. super(name, sub);
9. }
10.
11. @Override
12. public void update(){
13. System.out.println(String.format("%s %s 关闭股票行情,继续工作!", sub.getAction(), name));
14. }
15.
16. }
17.
“看NBA的同事”实现与“看股票的同事”类似,此处省略
测试
1. public class Test {
2.
3. public static void main(String[] args) {
4. // 老板
5. Boss boss = new Boss();
6.
7. // 看股票的同事
8. StockObserver observer1 = new StockObserver("哪路托", boss);
9. // 看NBA的同事
10. NBAObserver observer2 = new NBAObserver("啥事gay", boss);
11.
12. boss.attach(observer1);
13. boss.attach(observer2);
14.
15. boss.detach(observer1); // 主角光环!斑怎么样都打不过哪路托!所以减去
16.
17. // 斑复活了!
18. boss.setAction("我宇智波斑复活了!");
19. // 发出通知
20. boss.call();
21. }
22.
23. }
24.
测试结果
1. 我宇智波斑复活了! 啥事gay 关闭NBA 直播,继续工作!
2.
总结
- 动机是什么?将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象之间的一致性,我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。
- 什么时候使用?当一个对象的改变需要同时改变其他对象的时候,而且它不知道具体有多少对象待改变;一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立的改变和复用。
- 总的来讲,观察者模式所在的工作其实就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体,从而使得各自的变化都不会影响另一边的改变。
- 缺点?举个栗子,比如 VS2005,当你点击运行程序时,与运行程序以后,处理弹出一个控制台的程序窗体以外,工具栏发生了变化,工具箱不见了...。仅仅是点击了一个“运行”按钮,就发生这么多变化,而各个变化都涉及到不同的控件,并且没办法让每个控件都去实现一个“Observer”接口,也就是说不可能用接口的方式且实现观察模式。再回过头来看上面的栗子,尽管已经使用了依赖倒转原则,但是“抽象通知者”还是依赖“抽象观察者”,也就是说,万一没有了抽象观察者这样的接口,这通知的功能就完成不了了。另外就是每个具体观察者,他不一定是“更新”的方法要调用,就像刚才说的,我希望的是“工具箱”是隐藏,“自动窗口”是打开,这根本就不是同名的方法,这就是观察者模式不足的地方。在 .NET 中,可以用事件委托较好的解决该缺点,有兴趣的可以查看他人文章,此处不再叙述。
Pass:以上纯属个人理解~~如果发现有错或是心存建议意见等,欢迎大家评论或联系~(# ゚Д゚)~祝大家身体健康学习进步工作顺利生活愉快!
版权归 callmeDevil 所有,如需转载请标注转载来源
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(三):用.NET IoT库
· 【非技术】说说2024年我都干了些啥