设计模式之观察者模式

简介: 观察者模式(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

主要角色


  • 主题接口 Subject:管理所有的观察者以及数据变化后通知观察者。


  • 观察者接口 Observer:接受自己订阅的主题发布的数据。


  • 主题实现类。


  • 观察者实现类。


使用场景


  • 报社的业务就是出版报纸,客户订阅该报社,那么只要有新的报纸出版就会给订阅报社的客户送来,只要一直是报社的订阅客户,就能一直收到新报纸。


  • 当你不想订阅,取消就可以,就不会再收到通知。


  • 报社提供订阅与取消订阅的入口。


实际上这里就是一个观察者模式的例子,报社充当 Subject 主题角色,订阅报社的客户就是 Observer 观察者角色。出版者-主题,订阅者-观察者。


代码实现


实现一


首先我们定义 Subject 主题角色报社 NewspaperSubject。主要提供 注册观察者、删除观察者、通知所有观察者方法。


定义包报纸对象 Newspaper


public class Newspaper implements Serializable {
    private LocalDateTime reportTime;
    private String data;
    public LocalDateTime getReportTime() {
        return reportTime;
    }
    public void setReportTime(LocalDateTime reportTime) {
        this.reportTime = reportTime;
    }
    public String getData() {
        return data;
    }
    public void setData(String data) {
        this.data = data;
    }
    @Override
    public String toString() {
        return "Newspaper{" +
                "reportTime=" + reportTime +
                ", data='" + data + '\'' +
                '}';
    }
}


定义主题对象


public interface NewspaperSubject {
    /**
     * 注册观察者
     * @param observer
     */
    void registerObserver(Observer observer);
    /**
     * 移除观察者
     * @param observer
     */
    void removeObserver(Observer observer);
    /**
     * 通知所有观察者
     * @param data
     */
    void notifyObservers(Newspaper data);
}


同时实现该主题,代码如下:


import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ChinaNewspaperSubject implements NewspaperSubject {
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private List<Observer> observers;
    public ChinaNewspaperSubject() {
        this.observers = new ArrayList<>();
    }
    public void setChange() {
        Newspaper newspaper = new Newspaper();
        newspaper.setReportTime(LocalDateTime.now());
        newspaper.setData("发布新闻");
        notifyObservers(newspaper);
    }
    @Override
    public void registerObserver(Observer observer) {
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
        try {
            writeLock.lock();
            observers.add(observer);
        } finally {
            writeLock.unlock();
        }
    }
    @Override
    public void removeObserver(Observer observer) {
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
        try {
            writeLock.lock();
            observers.remove(observer);
        } finally {
            writeLock.unlock();
        }
    }
    @Override
    public void notifyObservers(Newspaper data) {
        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
        try {
            readLock.lock();
            observers.forEach(item -> item.notice(data));
        } finally {
            readLock.unlock();
        }
    }
}


然后定义观察者(Observer)角色 也就是报纸订阅者


public interface Observer {
    /**
     * 接收主题发布的更新通知
     */
    void notice(Newspaper data);
}


定义观察者具体实现类:一个香港用户订阅报纸


public class HonKongObserver implements Observer {
    @Override
    public void notice(Newspaper data) {
        System.out.println("我收到报社的报纸了:" + "内容是" + data.toString());
    }
}


最后测试


public class Test {
    public static void main(String[] args) {
        //创建报社
        ChinaNewspaperSubject newspaperSubject = new ChinaNewspaperSubject();
        //创建订阅者
        HonKongObserver honKongObserver = new HonKongObserver();
        //订阅者关注该报社
        newspaperSubject.registerObserver(honKongObserver);
        //报社发布新报纸。所有逇订阅者收到报纸
        newspaperSubject.setChange();
    }
}


方式二:通过JDK内置的实现


我们的JDK内部与为我们实现了观察者模式。只不过我们的主题需要继承 jdk 中的主题,观察者实现对应的Observer 接口。之前我们说过要多用组合与委托。面向接口编程而不是实现。内置的主题我们必须继承,若想更灵活其实我们自己定义主题接口会更好,并且也并不难。


首先我们的主题要先继承 Observerble ,这是jdk内置的。


public class NumsObservable extends Observable {
    public final static Integer ODD = 1;
    public final static Integer EVEN = 2;
    private int data = 0;
    /**
     * 获取对象数据
     *
     * @return
     */
    public int getData() {
        return data;
    }
    /**
     * 设置数据变化
     * 根据数据的变化设置相应的标志变量,通知给订阅者
     *
     * @param data
     */
    public void setData(int data) {
        this.data = data;
        Integer flag = EVEN;
        if ((this.data & 0x0001) == 1) {
            flag = ODD;
        }
        setChanged();
        // 将变化的变化的标识变量通知给订阅者
        notifyObservers(flag);
    }
}


接着定义我们的观察者:分别是偶数与奇数订阅者。


/**
 * 奇数内容订阅类
 * Created by jianqing.li on 2017/6/8.
 */
public class OddObserver implements Observer {
    /**
     * 继承自Observer接口类,update的方法的实现
     *
     * @param o   主题对象
     * @param arg notifyObservers(flag);传来的参数,即是标识变量
     */
    @Override
    public void update(Observable o, Object arg) {
        if (arg == NumsObservable.ODD) {
            NumsObservable numsObservable = (NumsObservable) o;
            System.out.println("Data has changed to ODD number " +           numsObservable.getData());
        }
    }
}


/**
 * 偶数内容订阅类:订阅主题的内容的偶数变化
 * Created by jianqing.li on 2017/6/8.
 */
public class EvenObserver implements Observer {
    /**
     * 继承自Observer接口类,update的方法的实现
     *
     * @param o   主题对象
     * @param arg notifyObservers(flag);传来的参数,即是标识变量
     */
    @Override
    public void update(Observable o, Object arg) {
        if (arg == NumsObservable.EVEN) {
            NumsObservable numsObservable = (NumsObservable) o;
            System.out.println("Data has changed to EVEN number " + numsObservable.getData());
        }
    }
}


编写测试


public class ObserverTest {
    public static void main(String[] args) {
        // 创建主题
        NumsObservable numsObservable = new NumsObservable();
        //创建订阅者
        OddObserver oddObserver = new OddObserver();
        EvenObserver evenObserver = new EvenObserver();
        numsObservable.addObserver(oddObserver);
        numsObservable.addObserver(evenObserver);
        //修改主题内容,触发notifyObservers
        numsObservable.setData(11);
        numsObservable.setData(12);
        numsObservable.setData(13);
    }
}


总结


  • java.util.Observable 的阴暗面:它是一个类,我们的主题必须继承它,我们若是想继承其他类就无能为力了。毕竟 Java 不能多重继承。


  • 在哪里有观察者模式的运用?嘿嘿谷歌的 Guava 类库中的 EventBus 事件总线使用的就是观察者模式。


  • Spring 中的 事件传播 也是如此。


  • 主要作用就是为交互对象之间的松耦合。当一个对象改变,依赖它的对象都会收到通知。主题并不知道观察者的细节,只知道观察者实现了 Observer 接口。
相关文章
|
2月前
|
设计模式 监控 Java
Kotlin - 改良设计模式 - 观察者模式
Kotlin - 改良设计模式 - 观察者模式
59 3
|
27天前
|
设计模式 存储 供应链
前端必须掌握的设计模式——观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,实现了一种订阅机制。它包含两个角色:**观察者**(订阅消息、接收通知并执行操作)和**被观察者**(维护观察者列表、发送通知)。两者通过一对多的关系实现解耦,当被观察者状态改变时,会通知所有订阅的观察者。例如,商店老板作为被观察者,记录客户的需求并在商品到货时通知他们。前端应用中,如DOM事件注册、MutationObserver等也体现了这一模式。
|
2月前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
|
3月前
|
设计模式 传感器
【设计模式】观察者模式(定义 | 特点 | Demo入门讲解)
【设计模式】观察者模式(定义 | 特点 | Demo入门讲解)
60 0
|
2月前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
2月前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
48 1
|
2月前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
32 3
|
3月前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
39 9
|
3月前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
44 2
|
3月前
|
设计模式 监控 UED
设计模式之观察者模式
【10月更文挑战第12天】 观察者模式是一种行为型设计模式,定义了一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都会自动更新。主要由主题(被观察者)和观察者组成,实现对象间的松耦合,广泛应用于用户界面、事件驱动系统和数据监控等领域。

热门文章

最新文章

  • 1
    设计模式转型:从传统同步到Python协程异步编程的实践与思考
    64
  • 2
    C++一分钟之-设计模式:工厂模式与抽象工厂
    54
  • 3
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    61
  • 4
    C++一分钟之-C++中的设计模式:单例模式
    79
  • 5
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    47
  • 6
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    81
  • 7
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    70
  • 8
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    54
  • 9
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    63
  • 10
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    137