前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >观察者模式

观察者模式

作者头像
田维常
发布2021-07-15 15:46:53
6510
发布2021-07-15 15:46:53
举报

大家好,我是老田,今天我给大家分享设计模式中的观察者模式。用贴切的生活故事,以及真实项目场景来讲设计模式,最后用一句话来总结这个设计模式。

故事

昨晚上,睡觉之前,我看了一段《兵法》,在第二十四卷中,看到这么一句:敌不动,我不动

看完这个后,我忽然想起一个设计模式:观察者模式

老田是个喜欢学习春秋战国时期的历史和人文故事,有通道之人,可以私聊!

为什么会想到哦观察者模式呢?请听老田慢慢道来。

本文目录:

关于设计模式系列,前面我们已经分享过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.

观察者模式的核心是将观察者与被观察者解耦,以类似消息/广播发送的机制联动两者,使被观察者的变动能通知到感兴趣的观察者们,从而做出相应的响应。

通用代码实现

观察者进行抽象:

代码语言:javascript
复制
//抽象观察者
public interface Observer {
    //反应
    void response();
}

两个观察者:

代码语言:javascript
复制
//观察者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作出反应!");
    }
}

被观察者(目标)进行抽象:

代码语言:javascript
复制
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(); //通知观察者方法
}

具体被观察者:

代码语言:javascript
复制
public class ConcreteSubject extends Subject {
    @Override
    public void notifyObserver() {
        System.out.println("具体目标发生改变...");
        System.out.println("--------------");
        for (Object obs : observers) {
            ((Observer) obs).response();
        }
    }
}

测试类:

代码语言:javascript
复制
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();
    }
}

运行结果:

代码语言:javascript
复制
具体目标发生改变...
--------------
具体观察者1作出反应!
具体观察者2作出反应!

通用代码UMML图

角色

UML图中,我们可以总结出,在观察者模式中有以下四个角色:

  • 抽象主题(Subject):指被观察的对象。该角色是一个抽象类或接口,定义了增加、删除、通知观察者对象的方法。
  • 具体主题(ConcreteSubject):具体被观察者,当其内部状态变化时,会通知已注册的观察者。
  • 抽象观察者(Observer):定义了响应通知的更新方法。
  • 具体观察者(ConcreteObserver1ConcreteObserver1):当得到状态更新的通知时,会自动做出响应。

下面我们来看看一个写生活中的观察者模式的场景。

观察者模式的应用场景

观察者模式在现实生活中的应用也非常广泛,比如:各种APP上的各种消息提示、学校铃声、公众号文章提示、各大网站消息提示等。用图解释:

.......

在软件系统中,当系统一方行为依赖另一方行为的变动时,可使用观察者模式松耦合联动双方,使得一方的变动可以通知到感兴趣的另一方对象,从而让另一方对象对此做出响应。

观察者模式主要适用于以下应用场景。

  • 当一个抽象模型包含两方面内容,其中一方面依赖另一方面。
  • 其他一个或多个对象的变化依赖另一个对象的变化。
  • 实现类似广播机制的功能,不需要知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播。
  • 多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知。

下面我们就用Java代码来实现观察者模式。

案例实现

JDK中的观察者模式

其实在JDK的util包内为我们提供了一套观察者模式的实现,在使用的时候我们只需要继承Observable和Observer类即可,其实观察者模式十分简单,推荐阅读JDK的实现代码真心没有几行。此外在JDK的实现中还增加了一个布尔类型的changed域,通过设置这个变量来确定是否通知观察者。

下面来实现一个微信给所用用户发送"端午安康":

代码语言:javascript
复制
//消息
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;
    }
}

平台给用户发消息

代码语言:javascript
复制
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;
    }
}

下面是用户收到消息:

代码语言:javascript
复制
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());
    }
}

测试类

代码语言:javascript
复制
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);
    }
}

运行结果:

代码语言:javascript
复制
微信 平台 给 用户们发送消息
兵哥 收到 微信 平台 的消息,内容:端午安康!
西哥 收到 微信 平台 的消息,内容:端午安康!
苗哥 收到 微信 平台 的消息,内容:端午安康!
勇哥 收到 微信 平台 的消息,内容:端午安康!
田哥 收到 微信 平台 的消息,内容:端午安康!

微信平台给大家发了"端午安康!",对应观察者就能收到"端午安康!"的消息。

在Observable中我们可以对观察者进行添加、删除以及消息通知等操作。

基于Guava API 实现观察者模式

在guava中,也有一套关于观察者模式的,具体实现如下:

添加maven依赖
代码语言:javascript
复制
<dependency>
   <groupId>com.google.guava</groupId>
   <artifactId>guava</artifactId>
   <version>20.0</version>
 </dependency>
创建Event实现类
代码语言:javascript
复制
public class GuavaEvent {
    //guava包下的注解
    @Subscribe
    public void subscribe(String str) {
        //业务逻辑
        System.out.println("执行 subscribe 方法,入参为 " + str);

    }
}

测试类:

代码语言:javascript
复制
public class GuavaEventTest {
    public static void main(String[] args) {
        EventBus eventBus = new EventBus();
        GuavaEvent guavaEven = new GuavaEvent();
        eventBus.register(guavaEven);
        eventBus.post("hello world!");
    }
}

运行结果:

代码语言:javascript
复制
执行 subscribe 方法,入参为 hello world!
小结

客户端只要创建一个EventBus,然后把我们实现的Event注册进去,再把对应的消息放进去,对应我们实现的Event就可以收到客户端发送的消息。

EventBus内部也提供来一系列的方法来供我们方便使用:

  • register 方法作为添加观察者
  • unregister方法删除观察者
  • post 方法发送通知消息等

使用起来非常方便。添加@Subscribe注解就可以创建一个订阅者了,具体的使用方式可以看看官网。

Spring中的观察者模式

在Spring中有一个ApplicationListener,也是采用观察者模式来处理的,ApplicationEventMulticaster作为主题,里面有添加,删除,通知等。

代码语言:javascript
复制
@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的监听。

通常在我们使用的时候是这样的:

代码语言:javascript
复制
xxxView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // do something
            }
});

这样我们就注册好了一个回调函数,这也是观察者模式的实现。

观察模式扩展

优缺点

优点
  • 观察者和被观察者是松耦合(抽象耦合)的,符合依赖倒置原则。
  • 分离了表示层(观察者)和数据逻辑层(被观察者),并且建立了一套触发机制,使得数据的变化可以响应到多个表示层上。
  • 实现了一对多的通信机制,支持事件注册机制,支持兴趣分发机制,当被观察者触发事件时,只有感兴趣的观察者可以接收到通知。
缺点
  • 如果观察者数量过多,则事件通知会耗时较长。
  • 事件通知呈线性关系,如果其中一个观察者处理事件卡壳,则会影响后续的观察者接收该事件。
  • 如果观察者和被观察者之间存在循环依赖,则可能造成两者之间的循环调用,导致系统崩溃。

总结

从本文内容,我们很容易看出,观察者模式其实是围绕了解耦的思想来写的,观察者模式作为行为型设计模式,主要也是为了不同的业务行为的代码解耦。最后一句话来总结:

敌不动,我不动

好了,今天的分享就到这里,希望大家能明白什么是观察者模式,也希望大家以后在面试的时候就不要再说你不会设计模式了。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-06-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java后端技术全栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 故事
  • 定义
  • 通用代码实现
    • 通用代码UMML图
      • 角色
      • 观察者模式的应用场景
      • 案例实现
        • JDK中的观察者模式
          • 基于Guava API 实现观察者模式
            • 添加maven依赖
            • 创建Event实现类
            • 小结
          • Spring中的观察者模式
            • 安卓开发中的观察者模式
            • 观察模式扩展
              • 优缺点
                • 优点
                • 缺点
            • 总结
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档