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

一、什么是观察者模式

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

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

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

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

  1. 主题Subject

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

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

  2. 抽象观察者Observer

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

11 public interface Observer { 2 //更新 23 public void update(); 4 }

   3. 具体主题

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

11 public class ConcreteSubject extends Subject { 2 23 //具体业务 34 public void doSomething() { 5 //... 46 super.notifyObserver(); 7 } 8 59 }

   4. 具体观察者

  实现Observer接口。

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

   5. Client客户端

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

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

   运行结果如下:

  

二、观察者模式的应用

  1. 何时使用

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

   2. 方法

  • 使用面向对象技术

   3. 优点

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

   4. 缺点

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

   5. 使用场景

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

  6. 应用实例

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

  7. 注意事项

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

三、观察者模式的实现

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

  1. 通知者接口

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

   2. 观察者

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

  3. 具体通知者

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

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

   4. 具体观察者

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

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

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

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

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

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

  

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

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

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

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

  

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

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

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

本文转自 https://www.cnblogs.com/adamjwh/p/10913660.html,如有侵权,请联系删除。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

观察者模式在spring中的应用

作者:王子源1观察者模式简介1.1定义指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布订阅模式、模型视图模式,它是对象行为型模式。

java设计模式详细讲解

原文链接:java设计模式详细讲解观察者模式(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.blogchina.cn%2Fblog%2Fguopengfei%2Fhome%2F270%2F1575725711626"java设计模式详细讲解观察者模式")如果想要更加

Android蓝牙连接汽车OBD设备

//设备连接public class BluetoothConnect implements Runnable {    private static final UUID CONNECT_UUID  UUID.fromString("0000110100001000800000805F9B34FB");

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