观察者模式
观察者模式
在前面三篇文章聊了构建者模式、单例模式、工厂模式,这三种都是属于创建型模式,接下来几篇文章来聊聊行为型模式里的一些设计模式;这篇文章的主题是观察者模式:在对象之间定义了一对多的关系,当一个对象发生改变时,依赖它的对象将收到通知并自动更新
怎么形象的理解这句话呢?
天气预报这类的应用大家都有用到过,现在有一个实时获取天气信息的目标类A,然后还有需要用到天气信息在界面显示的观察者类B,观察者类C,观察者类D等,这些观察者类要想目标类A在获取到数据后通知自己,就需要将自己注册到目标类A;然后目标类A在获取数据后就可以调用观察者类的更新方法,这样观察者类就能实时更新UI了
所以就可以知道观察者模式的实现方式就是:先定义观察者和目标的基类,目标需要提供维护观察者的方法(比如注册和解除注册),观察者需要提供更新接口;接下来具体的观察者和具体的目标就继承基类,具体观察者将自己注册到具体目标类中,这样当具体目标类发生变化时,就可以调用已注册的观察者的更新方法
与发布订阅模式区别
说到这里有没有发现它跟我们平时开发常用的一些发布-订阅框架很类似,但是它们两其实是有具体差异的;发布订阅框架逻辑:订阅者把自己想注册的事件注册到调度中心,当事件触发时,发布者将事件发布到订阅中心,由调度中心统一调度注册的订阅者
可以看到观察者模式和发布订阅模式的区别:
发布订阅模式统一由调度中心调度,而观察者模式由具体目标进行调度
发布订阅模式中订阅者和发布者不存在耦合,而观察者模式中观察者和目标存在耦合关系
其实也可以将发布订阅看做是观察者模式的一个变种,优化
实现
这里假设这么一个场景:有三个用户Tom、Lina、jams都关注了同一个音乐电台;当音乐电台有音乐发布时,将消息推送给三个用户;然后有一个用户认为这个电台音乐不太符合自己的风格,就决定取消关注;最后这个电台只能将消息推送给其余两个用户
定义观察者接口
/**
* Author:mango
* Time: 2019/4/9 11:06
* Desc: TODO(观察者接口)
*/
public interface Observer {
/**
* 观察者接收数据更新
* 参数以具体数据类型为准
*/
void updateData(String msg);
}
这个接口由具体观察者实现
定义目标接口
/**
* Author:mango
* Time: 2019/4/9 11:05
* Desc: TODO(目标接口)
*/
public interface Observedable {
/**
* 观察者调用:将自己注册到目标类
* @param observer
*/
void registerOberver(Observer observer);
/**
* 观察者调用:解除注册
* @param observer
*/
void unregisterOberver(Observer observer);
/**
* 目标类发布事件
* @param msg
*/
void pushEvent(String msg);
}
这个接口由目标类实现,也就是谁被观察由谁实现
定义具体观察者
/**
* Author:mango
* Time: 2019/4/9 11:12
* Desc: TODO(具体观察者)
*/
public class User implements Observer {
private String userName;
private String msg;
public User(String userName, String msg) {
this.userName = userName;
this.msg = msg;
}
@Override
public void updateData(String msg) {
this.msg = msg;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", msg='" + msg + '\'' +
'}';
}
}
定义具体目标
/**
* Author:mango
* Time: 2019/4/9 11:14
* Desc: TODO(目标对象)
*/
public class MusicRadio implements Observedable {
private List<Observer> userList;
public MusicRadio() {
userList = new ArrayList<>();
}
/**
* 注册观察者
* @param observer
*/
@Override
public void registerOberver(Observer observer) {
userList.add(observer);
}
/**
* 注销观察者
* @param observer
*/
@Override
public void unregisterOberver(Observer observer) {
if (userList.contains(observer)) {
userList.remove(observer);
observer.updateData("");
}
}
/**
* 向观察者推送消息
* @param msg
*/
@Override
public void pushEvent(String msg) {
for (Observer observer : userList) {
observer.updateData(msg);
}
}
/**
* 内部模拟消息产生,当然实际开发中这里可能是通过网络请求向服务器获取数据
* 或者服务器主动推送到这里
* @param msg
*/
public void produceMusic(String msg){
pushEvent(msg);
}
}
可以看到被观察对象,也就是目标对象内部维护了一个集合用来保存已经注册的观察者,只要有消息来了,就将消息推送给观察者
测试
public static void main(String[] args){
//被观察对象
Observedable musicRadio = new MusicRadio();
/**
* 实例化三个观察者
* 也就是三个用户,他们都观察musicRadio
*/
User tom = new User("Tom");
User lina = new User("Lina");
User jams = new User("Jams");
/**
* 注册观察者
*/
musicRadio.registerOberver(tom);
musicRadio.registerOberver(lina);
musicRadio.registerOberver(jams);
/**
* 创作了一首音乐,musicRadio内部将其消息推送给三个观察者
*/
musicRadio.pushEvent("春天");
//打印三个用户,看是否接收到消息了
System.out.print(tom+"\n");
System.out.print(lina+"\n");
System.out.print(jams+"\n");
System.out.print("=====================" + "\n");
//jams决定取消关注musicRadio
musicRadio.unregisterOberver(jams);
/**
* 创作了一首音乐,musicRadio内部将其消息推送给剩下的两个观察者
*/
musicRadio.pushEvent("时光春晓");
//打印三个用户,看谁接收到消息了
System.out.print(tom+"\n");
System.out.print(lina+"\n");
System.out.print(jams+"\n");
}
User{userName='Tom', msg='春天'}
User{userName='Lina', msg='春天'}
User{userName='Jams', msg='春天'}
=====================
User{userName='Tom', msg='时光春晓'}
User{userName='Lina', msg='时光春晓'}
User{userName='Jams', msg=''}
这里定义了三个用户,都关注了MusicRadio,然后MusicRadio推送了一首音乐,通过打印日志看到三个用户都接收到消息了;
当用户jams取消关注后,MusicRadio再推送音乐后就只能推送给剩下两个用户了
总结
通过面向接口的开发,实现松耦合,不论观察者是谁,怎么变,被观察者不需要修改;当被观察者变化时,比如从MusicRadio变成BookRadio,也只需要修改一处;这样就能极大增强程序的可扩展性;在Android中观察者模式的使用还是很多的,比如View点击事件的监听,就是一个注册观察者的过程;广播的使用也是一样的道理;当然生活中观察者模式的使用就很多了,比如你定闹钟,每天早上到了7点,它就会把你吵醒,这里你就是观察者,闹钟就是被观察者;多了解设计模式,不光对程序设计有好处,说不定对你的生活也能有意想不到的效果
————————————————
版权声明:本文为CSDN博主「渔夫在划桨」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_30993595/article/details/89138554