回复“000 ”获取程序员必备电子书
大家好,我是老田,今天我给大家分享设计模式中的 观察者模式
。用贴切的生活故事,以及真实项目场景来讲设计模式,最后用一句话来总结这个设计模式。
故事 昨晚上,睡觉之前,我看了一段《兵法》,在第二十四卷中,看到这么一句:敌不动,我不动
。
看完这个后,我忽然想起一个设计模式:观察者模式
。
老田是个喜欢学习春秋战国时期的历史和人文故事,有通道之人,可以私聊!
为什么会想到哦观察者模式呢?请听老田慢慢道来。
本文目录:
关于设计模式系列,前面我们已经分享过9种设计模式:
三国演义:责任链模式
韩信拜将:委派模式
3年工作必备 装饰器模式
工作五年了,居然还不懂 门面模式
点外卖,让我想起了 策略模式
初级必备:单例模式 的7个问题
快速掌握 模板方法 模式
五分钟 掌握 原型模式
泡图书馆,我想到了 享元模式
言归正传,我们先来看看观察者模式的定义。
定义 观察者模式(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内部也提供来一系列的方法来供我们方便使用:
使用起来非常方便。添加@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 } });
这样我们就注册好了一个回调函数,这也是观察者模式的实现。
观察模式扩展 优缺点 优点 观察者和被观察者是松耦合(抽象耦合)的,符合依赖倒置原则。 分离了表示层(观察者)和数据逻辑层(被观察者),并且建立了一套触发机制,使得数据的变化可以响应到多个表示层上。 实现了一对多的通信机制,支持事件注册机制,支持兴趣分发机制,当被观察者触发事件时,只有感兴趣的观察者可以接收到通知。 缺点 事件通知呈线性关系,如果其中一个观察者处理事件卡壳,则会影响后续的观察者接收该事件。 如果观察者和被观察者之间存在循环依赖,则可能造成两者之间的循环调用,导致系统崩溃。 总结 从本文内容,我们很容易看出,观察者模式其实是围绕了解耦
的思想来写的,观察者模式作为行为型设计模式,主要也是为了不同的业务行为的代码解耦
。最后一句话来总结:
敌不动,我不动
好了,今天的分享就到这里,希望大家能明白什么是观察者模式,也希望大家以后在面试的时候就不要再说你不会设计模式了。
如果有技术探讨、需要学习资源等,请加我微信。