简说设计模式——观察者模式

一、什么是观察者模式

  观察者一般可以看做是第三者,比如在学校上自习的时候,大家肯定都有过交头接耳、各种玩耍的经历,这时总会有一个“放风”的小伙伴,当老师即将出现时及时“通知”大家老师来了。再比如,拍卖会的时候,大家相互叫价,拍卖师会观察最高标价,然后通知给其它竞价者竞价,这就是一个观察者模式。

  对于观察者模式而言,肯定有观察者和被观察者之分。比如在一个目录下建立一个文件,这时系统会通知目录管理器增加目录,并通知磁盘减少空间,在这里,文件就是观察者,目录管理器和磁盘就是被观察者。

  观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。UML结构图如下:

  其中,Subject类是主题,它把所有对观察者对象的引用文件存在了一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供了一个接口,可以增加和删除观察者对象;Observer类是抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己;ConcreteSubject类是具体主题,将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知;ConcreteObserver是具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协同。

  1. 主题Subject

  首先定义一个观察者数组,并实现增、删及通知操作。它的职责很简单,就是定义谁能观察,谁不能观察,用Vector是线程同步的,比较安全,也可以使用ArrayList,是线程异步的,但不安全。

1 public class Subject {
 2
 3     //观察者数组
 4     private Vector<Observer> oVector = new Vector<>();
 5
 6     //增加一个观察者
 7     public void addObserver(Observer observer) {
 8         this.oVector.add(observer);
 9     }
10
11     //删除一个观察者
12     public void deleteObserver(Observer observer) {
13         this.oVector.remove(observer);
14     }
15
16     //通知所有观察者
17     public void notifyObserver() {
18         for(Observer observer : this.oVector) {
19             observer.update();
20         }
21     }
22
23 }

  2. 抽象观察者Observer

  观察者一般是一个接口,每一个实现该接口的实现类都是具体观察者。

1 public interface Observer {
2     //更新
3     public void update();
4 }

  3. 具体主题

  继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种。

1 public class ConcreteSubject extends Subject {
2
3     //具体业务
4     public void doSomething() {
5         //...
6         super.notifyObserver();
7     }
8
9 }

  4. 具体观察者

  实现Observer接口。

1 public class ConcreteObserver implements Observer {
2
3     @Override
4     public void update() {
5         System.out.println("收到消息,进行处理");
6     }
7
8 }

  5. Client客户端

  首先创建一个被观察者,然后定义一个观察者,将该被观察者添加到该观察者的观察者数组中,进行测试。

1 public class Client {
 2
 3     public static void main(String[] args) {
 4         //创建一个主题
 5         ConcreteSubject subject = new ConcreteSubject();
 6         //定义一个观察者
 7         Observer observer = new ConcreteObserver();
 8         //观察
 9         subject.addObserver(observer);
10         //开始活动
11         subject.doSomething();
12     }
13
14 }

  运行结果如下:

  

二、观察者模式的应用

  1. 何时使用

  • 一个对象状态改变,所有的依赖对象都将得到通知

  2. 方法

  • 使用面向对象技术

  3. 优点

  • 观察者和被观察者是抽象耦合的
  • 建立了一套触发机制

  4. 缺点

  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
  • 如果观察者和观察目标间有循环依赖,可能导致系统崩溃
  • 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的

  5. 使用场景

  • 关联行为场景
  • 事件多级触发场景
  • 跨系统的消息变换场景,如消息队列的处理机制

  6. 应用实例

  • 手机丢了,委托别人给其他人发消息通知
  • 通知老师/老板来了
  • 拍卖,拍卖师观察最高标价,然后通知给其它竞价者竞价
  • 在一个目录下建立一个文件,会同时通知目录管理器增加目录,并通知磁盘减少空间,文件是被观察者,目录管理器和磁盘管理器是观察者
  • 猫叫了一声,吓着了老鼠,也惊到了主人,猫是被观察者,老鼠和人是观察者

  7. 注意事项

  • 避免循环引用
  • 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式

三、观察者模式的实现

  下面举一个具体实例,假设上班时间有一部分同事在看股票,一部分同事在看NBA,这时老板回来了,前台通知了部分同事老板回来了,这些同事及时关闭了网页没被发现,而没被通知到的同事被抓了个现行,被老板亲自“通知”关闭网页,UML图如下:

  1. 通知者接口

1 public interface Subject {
 2
 3     //增加
 4     public void attach(Observer observer);
 5     //删除
 6     public void detach(Observer observer);
 7     //通知
 8     public void notifyObservers();
 9
10     //状态
11     public void setAction(String action);
12     public String getAction();
13
14 }

  2. 观察者

1 public abstract class Observer {
 2
 3     protected String name;
 4     protected Subject subject;
 5
 6     public Observer(String name, Subject subject) {
 7         this.name = name;
 8         this.subject = subject;
 9     }
10
11     public abstract void update();
12
13 }

  3. 具体通知者

  前台Secretary和老板Boss作为具体通知者,实现Subject接口。这里只给出Secretary类的代码,Boss类与之类似。

1 public class Secretary implements Subject {
 2
 3     //同事列表
 4     private List<Observer> observers = new LinkedList<>();
 5     private String action;
 6
 7     //添加
 8     @Override
 9     public void attach(Observer observer) {
10         observers.add(observer);
11     }
12
13     //删除
14     @Override
15     public void detach(Observer observer) {
16         observers.remove(observer);
17     }
18
19     //通知
20     @Override
21     public void notifyObservers() {
22         for(Observer observer : observers) {
23             observer.update();
24         }
25     }
26
27     //前台状态
28     @Override
29     public String getAction() {
30         return action;
31     }
32
33     @Override
34     public void setAction(String action) {
35         this.action = action;
36     }
37
38 }

  4. 具体观察者

  StockObserver是看股票的同事,NBAObserver是看NBA的同事,作为具体观察者,继承Observer类。这里只给出StockObserver类的代码,NBAObserver类与之类似。

1 public class StockObserver extends Observer {
 2
 3     public StockObserver(String name, Subject subject) {
 4         super(name, subject);
 5     }
 6
 7     @Override
 8     public void update() {
 9         System.out.println(subject.getAction() + "\n" + name + "关闭股票行情,继续工作");
10     }
11
12 }

  5. 前台作为通知者进行通知(Client)

  前台作为通知者,通知观察者。这里添加adam和tom到通知列表,并从通知列表中删除了adam,测试没在通知列表中的对象不会收到通知。

1 public class Client {
 2
 3     public static void main(String[] args) {
 4         //前台为通知者
 5         Secretary secretary = new Secretary();
 6
 7         StockObserver observer = new StockObserver("adam", secretary);
 8         NBAObserver observer2 = new NBAObserver("tom", secretary);
 9
10         //前台通知
11         secretary.attach(observer);
12         secretary.attach(observer2);
13
14         //adam没被前台通知到,所以被老板抓了个现行
15         secretary.detach(observer);
16
17         //老板回来了
18         secretary.setAction("小心!Boss回来了!");
19         //发通知
20         secretary.notifyObservers();
21     }
22
23 }

  运行结果如下,只有tom接收到了通知:

  

  6. 老板作为通知者进行通知(Client)

  老板作为通知者,通知观察者。这里将tom从老板的通知列表中移除,老板只通知到了adam。

1 public class Client {
 2
 3     public static void main(String[] args) {
 4         //老板为通知者
 5         Boss boss = new Boss();
 6
 7         StockObserver observer = new StockObserver("adam", boss);
 8         NBAObserver observer2 = new NBAObserver("tom", boss);
 9
10         //老板通知
11         boss.attach(observer);
12         boss.attach(observer2);
13
14         //tom没被老板通知到,所以不用挨骂
15         boss.detach(observer2);
16
17         //老板回来了
18         boss.setAction("咳咳,我大Boss回来了!");
19         //发通知
20         boss.notifyObservers();
21     }
22
23 }

  运行结果如下,只有adam挨骂了: 

  

  当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象有待改变的时候,应该考虑使用观察者模式。

  而使用观察者模式的动机在于:将一个系统分割成一系列相互协作的类有一个很不好的副作用,就是需要维护相关对象间的一致性,我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便,而观察者模式所做的工作就是在解除耦合

  源码地址:https://gitee.com/adamjiangwh/GoF

(0)

相关推荐

  • 设计模式(20) 观察者模式

    观察者模式是一种平时接触较多的模式.它主要用于一对多的通知发布机制,当一个对象发生改变时自动通知其他对象,其他对象便做出相应的反应,同时保证了被观察对象与观察对象之间没有直接的依赖. GOF对观察者模 ...

  • 通俗易懂系列 | 设计模式(七):观察者模式

    介绍# 观察者模式是行为设计模式之一.当您对对象的状态感兴趣并希望在有任何更改时收到通知时,观察者设计模式非常有用.在观察者模式中,监视另一个对象状态的对象称为Observer,正在被监视的对象称为S ...

  • 设计模式-行为型-观察者模式

    观察者模式(Observer): 指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新. 观察者模式的角色: 1)抽象目标(Subject):也叫抽象 ...

  • 设计模式中的观察者模式

    观察者模式是一种软件设计模式,其中一个名为主体(Subject)的对象维护其依赖项列表,称为观察者,并通常通过调用它们(observers)的方法之一来自动通知它们任何状态更改. 观察者模式主要用于在 ...

  • 设计模式之观察者模式

    设计模式之观察者模式

  • PHP设计模式—观察者模式

    定义: 观察者模式(Observer):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 结构: Eve ...

  • 设计模式--观察者模式

    设计模式--观察者模式

  • 简说设计模式——迭代器模式

    一.什么是迭代器模式 迭代器这个词在Java中出现过,即Java中使用Iterator迭代器对集合进行遍历,但迭代器模式算是一个没落的模式,基本上没人会单独写一个迭代器,除非是产品性质的开发. 迭代器 ...

  • 简说设计模式——访问者模式

    一.什么是访问者模式 访问者模式是一个相对比较简单,但结构又稍显复杂的模式,它讲的是表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作.例如,你在 ...

  • 简说设计模式——中介者模式

    一.什么是中介者模式 中介这个词并不陌生,就是房屋中介的那个"中介",就是中间人的意思.比如MVC模式,C(Controller控制器)是M(Model模型)和V(View视图)的 ...

  • 简说设计模式——解释器模式

    一.什么是解释器模式 解释器这个名词想必大家都不会陌生,比如编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树.诸如此类 ...

  • 简说设计模式——状态模式

    一.什么是状态模式 状态这个词汇我们并不陌生,在日常生活中,不同时间就有不同的状态,早上起来精神饱满,中文想睡觉,下午又渐渐恢复,晚上可能精神更旺也可能耗费体力只想睡觉,这一天中就对应着不同的状态.或 ...

  • 简说设计模式——命令模式

    一.什么是命令模式 在说命令模式前我们先来说一个小例子.很多人都有吃夜市的经历,对于那些推小车的摊位,通常只有老板一个人,既负责制作也负责收钱,我要两串烤串多放辣,旁边的人要了三串烤面筋不要辣,过了一 ...

  • 结合JDK源码看设计模式——观察者模式

    前言: 现在我们生活中已经离不开微信,QQ等交流软件,这对于我们来说不仅是交流,更有在朋友圈中或空间中进行分享自己的生活,同时也可以通过这个渠道知道别人的生活.我们在看朋友圈的时候其实我们扮演的就是一 ...