写点什么

ObserverPattern- 观察者模式

作者:梁歪歪 ♚
  • 2022 年 6 月 01 日
  • 本文字数:3477 字

    阅读完需:约 11 分钟

ObserverPattern-观察者模式

观察者模式

观察者模式(Observer Pattern):也叫做发布订阅模式,其定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新,观察者模式属于行为型模式。


这里观察者模式有两种:push 模式,pull 模式


push 模式


示例:我们以获取天气预报中的气温来举例说明


  • 观察者接口Observer.java


package cn.liangyy.observer;
/** * 观察者接口 */public interface Observer { void update(float temperature); //更新天气信息}
复制代码


  • 管理观察者Subject.java


package cn.liangyy.observer;
/** * 管理观察者 */public interface Subject { void registerObserver(Observer o); //注册观察对象 void removeObserver(Observer o); //移除观察对象 void notifyObservers(); //通知观察对象}
复制代码


  • 天气数据WeatherData.java,就相当于是一个被观察者


package cn.liangyy.observer;
import java.util.ArrayList;import java.util.List;
/** * 被观察者 * 天气数据 */public class WeatherData implements Subject { //观察者不止一个,所以用list进行维护 private List<Observer> observers;
//温度 private float temperature;
public void setMessurements(float temperature){ this.temperature = temperature; //气温信息发生变化时,通知所有观察者 notifyObservers(); }
public WeatherData(){ //初始化list this.observers = new ArrayList<>(); }
/** * 注册观察者 * @param o */ @Override public void registerObserver(Observer o) { observers.add(o); }
/** * 移除观察者 * @param o */ @Override public void removeObserver(Observer o) { int i = observers.indexOf(o); if (i >= 0){ observers.remove(i); } }
/** * 通知所有观察者 */ @Override public void notifyObservers() { //遍历所有的观察者 for (Observer observer : observers){ //通知观察者更新数据信息 observer.update(temperature); } }}
复制代码


  • 观察者类WeatherDisplay.java


package cn.liangyy.observer;
/** * 观察者类 */public class WeatherDisplay implements Observer { //维护观察者 private Subject subject; //温度 private float temperature;
public WeatherDisplay(Subject subject){ //注册监听对象 this.subject = subject; subject.registerObserver(this); }
@Override public void update(float temperature) { //当被观察者气温发生变化会调用这个方法,也就等于更新了观察者对象的数据 this.temperature = temperature; }
public void display(){ System.out.println("当前最新的温度是:"+temperature); }}
复制代码


  • 测试


package cn.liangyy.observer;
/** * 观察者模式-测试 */public class TestWeather { public static void main(String[] args) { //天气数据即被观察者 WeatherData weatherData = new WeatherData(); //天气展示即观察者 WeatherDisplay weatherDisplay = new WeatherDisplay(weatherData); //被观察者数据发生变化了,其内部会通知观察者 weatherData.setMessurements(16.2f); //查看观察者是否获取到了最新温度数据 weatherDisplay.display(); }}
复制代码


由上述例子可以看到,我们只是设置了 WeatherData 对象中的温度,但是 WeatherDisplay 中也实时改变了温度,这就是观察者模式。那么这种 push 模式,是观察者主动推的数据给观察者,不论观察者是不是需要数据,都会被推送,那么由一个问题:就是数据量很大,但是我只要其中的一部分数据,该怎么办呢??


pull 模式


示例:我们以空间好友自行去获取好友发表在空间的动态来举例说明(这里利用 JDK 的观察者模式来实现一个 pull 类型的观察者模式)


  • 空间动态类Trends.java


package cn.liangyy.observer.pull;
/** * 空间动态类 * 记录动态信息 */public class Trends { //发表动态的用户昵称 private String nickName; //发表的动态内容 private String content;
public String getNickName() { return nickName; }
public void setNickName(String nickName) { this.nickName = nickName; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }}
复制代码


  • 空间类Zone.java,被观察者,继承自 JDK 自带的被观察者对象:Observable


package cn.liangyy.observer.pull;
import java.util.Observable;
/** * 空间类(被观察者) */public class Zone extends Observable { //发表动态 public void publishTrends(Trends trends){ System.out.println(trends.getNickName()+" 发表了一个动态【"+trends.getContent()+"】"); //占位,只是设置一个标记说明数据改变了 setChanged(); //通知所有观察者 notifyObservers(trends); }}
复制代码


  • 好友类Friends.java,观察者,实现 JDK 自带的观察者接口Observer


package cn.liangyy.observer.pull;
import java.util.Observable;import java.util.Observer;
/** * 好友类(观察者) */public class Friends implements Observer { //看动态的人的名字 private String name; public String getName(){ return name; }
public void setName(String name){ this.name = name; }
/** * This method is called whenever the observed object is changed. An * application calls an <tt>Observable</tt> object's * <code>notifyObservers</code> method to have all the object's * observers notified of the change. * * @param o the observable object. * @param arg an argument passed to the <code>notifyObservers</code> */ @Override public void update(Observable o, Object arg) { //获取(空间)被观察者数据 Trends trends = new Trends(); if (null != arg && arg instanceof Trends){ trends = (Trends) arg; } System.out.println(this.getName()+",您好!您收到了来自 "+trends.getNickName()+" 的一条动态【"+trends.getContent()+"】,快去点赞吧!"); }}
复制代码


  • 测试


package cn.liangyy.observer.pull;
/** * 观察者模式-测试 */public class TestObserver { public static void main(String[] args) { Zone zone = new Zone(); //观察者,即查看好友动态的人 Friends friends = new Friends(); friends.setName("亚索");
//被观察者,即发送动态的人 Trends trends = new Trends(); trends.setNickName("瑞雯"); trends.setContent("此生唯爱亚索!"); //注册观察者 zone.addObserver(friends); //发布动态 zone.publishTrends(trends); }}
复制代码



可以看到,当动态发布之后,好友立即就收到了动态,而这里我们是主动去获取数据的,即Friends类的 update()方法。我们可以根据需求拿该拿的数据。


JDK 自带的观察者模式的局限性


  • Observable 是一个类而不是一个接口,所以复用性就不是很好,如果某类同时具有 Observable 类和另一个超类的行为,就会有问题。(Java 不支持多继承)

  • Observable 将关键的方法保护起来了,比如 setChange 方法,也就是我们只能继承 Observable 类才能实现想要的功能,这个违反了合成复用原则。


观察者模式的使用场景‘一般适用于需要实时监听数据的场景,比如事件监听器等。


观察者模式的优点观察者和被观察者之间建立一个抽象的耦合,要扩展观察者只需要新建观察者并注册进去就可以了,扩展性好。


观察者模式的缺点


  • 观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度

  • 当观察者对象过多时,会使得系统非常复杂难以维护

发布于: 刚刚阅读数: 3
用户头像

梁歪歪 ♚

关注

还未添加个人签名 2021.07.22 加入

还未添加个人简介

评论

发布
暂无评论
ObserverPattern-观察者模式_设计模式_梁歪歪 ♚_InfoQ写作社区