观察者模式
回复“000”获取程序员必备电子书
大家好,我是老田,今天我给大家分享设计模式中的观察者模式
。用贴切的生活故事,以及真实项目场景来讲设计模式,最后用一句话来总结这个设计模式。
故事
昨晚上,睡觉之前,我看了一段《兵法》,在第二十四卷中,看到这么一句:敌不动,我不动
。
看完这个后,我忽然想起一个设计模式:观察者模式
。
老田是个喜欢学习春秋战国时期的历史和人文故事,有通道之人,可以私聊!
为什么会想到哦观察者模式呢?请听老田慢慢道来。
本文目录:
关于设计模式系列,前面我们已经分享过9种设计模式:
言归正传,我们先来看看观察者模式的定义。
定义
观察者模式(Observer Pattern
)又叫作发布-订阅(Publish/Subscribe
)模式、模型-视图(Model/View
)模式、源-监听器(Source/Listener
)模式或从属者(Dependent
)模式。
定义一种一对多的依赖关系,一个主题对象可被多个观察者对象同时监听,使得每当主题对象状态变化时,所有依赖它的对象都会得到通知并被自动更新,属于行为型设计模式。
英文定义:
Defines a one-to-many dependency relationship betweenobjects so that each time an object's state changes,its dependentobjects are notified and automatically updated.
观察者模式的核心是将观察者与被观察者解耦,以类似消息/广播发送的机制联动两者,使被观察者的变动能通知到感兴趣的观察者们,从而做出相应的响应。
通用代码实现
观察者进行抽象:
//抽象观察者
public interface Observer {
//反应
void response();
}
两个观察者:
//观察者1
public class ConcreteObserver1 implements Observer {
@Override
public void response() {
System.out.println("具体观察者1作出反应!");
}
}
//观察者2
public class ConcreteObserver2 implements Observer {
@Override
public void response() {
System.out.println("具体观察者2作出反应!");
}
}
被观察者(目标)进行抽象:
import java.util.ArrayList;
import java.util.List;
//抽象目标
public abstract class Subject {
protected List<Observer> observers = new ArrayList<Observer>();
//增加观察者方法
public void add(Observer observer) {
observers.add(observer);
}
//删除观察者方法
public void remove(Observer observer) {
observers.remove(observer);
}
public abstract void notifyObserver(); //通知观察者方法
}
具体被观察者:
public class ConcreteSubject extends Subject {
@Override
public void notifyObserver() {
System.out.println("具体目标发生改变...");
System.out.println("--------------");
for (Object obs : observers) {
((Observer) obs).response();
}
}
}
测试类:
public class ObserveTest {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
Observer obs1 = new ConcreteObserver1();
Observer obs2 = new ConcreteObserver2();
subject.add(obs1);
subject.add(obs2);
subject.notifyObserver();
}
}
运行结果:
具体目标发生改变...
--------------
具体观察者1作出反应!
具体观察者2作出反应!
通用代码UMML图
角色
从UML
图中,我们可以总结出,在观察者模式中有以下四个角色:
抽象主题( Subject
):指被观察的对象。该角色是一个抽象类或接口,定义了增加、删除、通知观察者对象的方法。具体主题( ConcreteSubject
):具体被观察者,当其内部状态变化时,会通知已注册的观察者。抽象观察者( Observer
):定义了响应通知的更新方法。具体观察者( ConcreteObserver1
、ConcreteObserver1
):当得到状态更新的通知时,会自动做出响应。
下面我们来看看一个写生活中的观察者模式的场景。
观察者模式的应用场景
观察者模式在现实生活中的应用也非常广泛,比如:各种APP上的各种消息提示、学校铃声、公众号文章提示、各大网站消息提示等。用图解释:
.......
在软件系统中,当系统一方行为依赖另一方行为的变动时,可使用观察者模式松耦合联动双方,使得一方的变动可以通知到感兴趣的另一方对象,从而让另一方对象对此做出响应。
观察者模式主要适用于以下应用场景。
当一个抽象模型包含两方面内容,其中一方面依赖另一方面。 其他一个或多个对象的变化依赖另一个对象的变化。 实现类似广播机制的功能,不需要知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播。 多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知。
下面我们就用Java代码来实现观察者模式。
案例实现
JDK中的观察者模式
其实在JDK的util包内为我们提供了一套观察者模式的实现,在使用的时候我们只需要继承Observable和Observer类即可,其实观察者模式十分简单,推荐阅读JDK的实现代码真心没有几行。此外在JDK的实现中还增加了一个布尔类型的changed域,通过设置这个变量来确定是否通知观察者。
下面来实现一个微信给所用用户发送"端午安康":
//消息
public class Message {
private String content;
public Message(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
平台给用户发消息
import java.util.Observable;
//APP平台
public class App extends Observable {
private String name;
public App(String name) {
this.name = name;
}
public void publishMsg(Message message) {
System.out.println(this.name + " 平台 给 用户们发送消息");
//setChanged是Observable中的方法
setChanged();
//notifyObservers也是Observable中的方法
notifyObservers(message);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
下面是用户收到消息:
import java.util.Observable;
import java.util.Observer;
public class User implements Observer {
private String userName;
public User(String userName) {
this.userName = userName;
}
//实现了Observer的update方法
@Override
public void 的update方法(Observable o, Object arg) {
App app=(App)o;
Message message=(Message)arg;
System.out.println(this.userName+" 收到 "+ app.getName()+" 平台 的消息,内容:"+message.getContent());
}
}
测试类
public class JDKObserverTest {
public static void main(String[] args) {
App app = new App("微信");
Message message = new Message("端午安康!");
User tian = new User("田哥");
app.addObserver(tian);
User zhang = new User("勇哥");
app.addObserver(zhang);
User li = new User("苗哥");
app.addObserver(li);
User xi = new User("西哥");
app.addObserver(xi);
User bing = new User("兵哥");
app.addObserver(bing);
app.publishMsg(message);
}
}
运行结果:
微信 平台 给 用户们发送消息
兵哥 收到 微信 平台 的消息,内容:端午安康!
西哥 收到 微信 平台 的消息,内容:端午安康!
苗哥 收到 微信 平台 的消息,内容:端午安康!
勇哥 收到 微信 平台 的消息,内容:端午安康!
田哥 收到 微信 平台 的消息,内容:端午安康!
微信平台给大家发了"端午安康!",对应观察者就能收到"端午安康!"的消息。
在Observable中我们可以对观察者进行添加、删除以及消息通知等操作。
基于Guava API 实现观察者模式
在guava中,也有一套关于观察者模式的,具体实现如下:
添加maven依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
创建Event实现类
public class GuavaEvent {
//guava包下的注解
@Subscribe
public void subscribe(String str) {
//业务逻辑
System.out.println("执行 subscribe 方法,入参为 " + str);
}
}
测试类:
public class GuavaEventTest {
public static void main(String[] args) {
EventBus eventBus = new EventBus();
GuavaEvent guavaEven = new GuavaEvent();
eventBus.register(guavaEven);
eventBus.post("hello world!");
}
}
运行结果:
执行 subscribe 方法,入参为 hello world!
小结
客户端只要创建一个EventBus,然后把我们实现的Event注册进去,再把对应的消息放进去,对应我们实现的Event就可以收到客户端发送的消息。
EventBus内部也提供来一系列的方法来供我们方便使用:
register 方法作为添加观察者 unregister方法删除观察者 post 方法发送通知消息等 使用起来非常方便。添加@Subscribe注解就可以创建一个订阅者了,具体的使用方式可以看看官网。
Spring中的观察者模式
在Spring中有一个ApplicationListener,也是采用观察者模式来处理的,ApplicationEventMulticaster作为主题,里面有添加,删除,通知等。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
另外,Spring中的ContextLoaderListener
实现了ServletContextListener
接口,ServletContextListener
接口又继承了EventListener
,在JDK中,EventListener
有非常广泛的应用。
其实,我们在很多框架中,只要看到XxxListener样式的类,基本上都是观察者模式的实现。
安卓开发中的观察者模式
在Android中我们有一个常用的回调:对于View点击事件的监听。现在我们就来分析一下对于View的监听。
通常在我们使用的时候是这样的:
xxxView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// do something
}
});
这样我们就注册好了一个回调函数,这也是观察者模式的实现。
观察模式扩展
优缺点
优点
观察者和被观察者是松耦合(抽象耦合)的,符合依赖倒置原则。 分离了表示层(观察者)和数据逻辑层(被观察者),并且建立了一套触发机制,使得数据的变化可以响应到多个表示层上。 实现了一对多的通信机制,支持事件注册机制,支持兴趣分发机制,当被观察者触发事件时,只有感兴趣的观察者可以接收到通知。
缺点
如果观察者数量过多,则事件通知会耗时较长。 事件通知呈线性关系,如果其中一个观察者处理事件卡壳,则会影响后续的观察者接收该事件。 如果观察者和被观察者之间存在循环依赖,则可能造成两者之间的循环调用,导致系统崩溃。
总结
从本文内容,我们很容易看出,观察者模式其实是围绕了解耦
的思想来写的,观察者模式作为行为型设计模式,主要也是为了不同的业务行为的代码解耦
。最后一句话来总结:
敌不动,我不动
好了,今天的分享就到这里,希望大家能明白什么是观察者模式,也希望大家以后在面试的时候就不要再说你不会设计模式了。
如果有技术探讨、需要学习资源等,请加我微信。