通俗易懂的java设计模式之观察者模式

现在有这么一个需求让你设计一个程序,有观察对象和被观察对象,要求是被观察者属性值发生变化后,观察者能感知到这个变化,你会怎么设计该程序?

其实该问题有很多种实现方式,比如把被观察者的值放入一个缓存中,然后不断进行循环将被观察者属性最新的值和缓存里的旧值进行比较,当存在不一致情况,那么就说明被观察对象发生了变化。

但这种方式存在哪些问题呢。

  1. 不断循环遍历,浪费CPU资源

  2. 如果被观察者有些私有属性值发生变化,只能通过反射获取。

那么有更好的方式吗?不要着急,为了让大家更好的理解,我举一个生活的例子来说明。

一个家长在监督孩子写作业,请问家长如何做最省力高效,是一直看着孩子做作业,还是等家长过一会问一下作业的完成情况?都不是,最省力高效的做法是孩子完成作业的那一刻,主动的告知家长,让家长进行作业的检查。同样应用在程序中亦是如此,这也是观察者模式的精髓所在。


观察者模式包含以下几个核心角色:

  • 主题(Subject):也称为被观察者或可观察者,它是具有状态的对象,并维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。
  • 观察者(Observer):观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。
  • 具体主题(Concrete Subject):具体主题是主题的具体实现类。它维护着观察者列表,并在状态发生改变时通知观察者。
  • 具体观察者(Concrete Observer):具体观察者是观察者的具体实现类。它实现了更新方法,定义了在收到主题通知时需要执行的具体操作。

观察者模式的关键代码是在被观察对象下有一个 ArrayList 存放观察者们,这样在自己发生变化后,就能通知到所有观察者(循环)。

参考代码如下:

csharp 复制代码
public class ObserverMode {
    public static void main(String[] args) {
        Subject subject = new Subject();
        new HexaObserver(subject);
        new OctalObserver(subject);
        new BinaryObserver(subject);
        System.out.println("First state change: 15");
        subject.setState(15);
        System.out.println("Second state change: 10");
        subject.setState(10);
    }
}

class Subject {
    private List<Observer> observers
            = new ArrayList<Observer>();
    private int state;
    public int getState() {
        return state;
    }
    public void setState(int state) {
        this.state = state;
        notifyAllObservers();
    }
    public void attach(Observer observer){
        observers.add(observer);
    }
    //注销方法,用于在观察者集合中删除一个观察者
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    public void notifyAllObservers(){
        for (Observer observer : observers) {
            observer.update();
        }
    }
}
abstract class Observer {
    protected Subject subject;
    public abstract void update();
}
class BinaryObserver extends Observer{
    public BinaryObserver(Subject subject){
        this.subject = subject;
        this.subject.attach(this);
    }
    @Override
    public void update() {
        System.out.println( "Binary String: "
                + Integer.toBinaryString( subject.getState() ) );
    }
}
class OctalObserver extends Observer{
    public OctalObserver(Subject subject){
        this.subject = subject;
        this.subject.attach(this);
    }
    @Override
    public void update() {
        System.out.println( "Octal String: "
                + Integer.toOctalString( subject.getState() ) );
    }
}
class HexaObserver extends Observer{

    public HexaObserver(Subject subject){
        this.subject = subject;
        this.subject.attach(this);
    }
    @Override
    public void update() {
        System.out.println( "Hex String: "
                + Integer.toHexString( subject.getState() ).toUpperCase() );
    }
}

通过代码可以看到,被观察者分别给到了3个观察者对象,观察者对象通过attach方法将自身注册到了被观察者维护的Arraylist列表中。被观察者的set方法中有个通知观察者的方法,可以在属性赋值后通知给所有观察者,这是一套标准的观察者模式用法。

但这种模式就没有缺点了吗?它最大的问题就是违反了单一职责原则(被观察者还要维护一个观察者列表),观察者和被观察者耦合度高。为了解决此问题,可以采用事件驱动的方式,被观察发生的变化可以当做一个事件。每当被观察者发生变化后,就在事件列表里存放该事件即可。这样观察者只需要循环遍历事件列表有无事件就能知道被观察对象是否发生了变化。

程序的设计源于生活,我们要善于发现生活的细节,并应用在程序中。

结束!

相关推荐
hlsd#4 分钟前
go 集成swagger 在线接口文档
开发语言·后端·golang
大脑经常闹风暴@小猿23 分钟前
Django 启用国际化支持—实现配置多国语言
后端·python·django
GISer_Jing24 分钟前
Javascript——设计模式(一)
前端·javascript·设计模式
计算机-秋大田1 小时前
基于微信小程序的在线疫苗预约的设计与实现,LW+源码+讲解
spring boot·后端·微信小程序·小程序·vue
不修×蝙蝠1 小时前
后端总指挥---文件接口
后端·文件接口·总指挥
八宝袋1 小时前
设计模式之工厂模式,但是宝可梦
java·设计模式·工厂模式
oioihoii2 小时前
设计模式概述
java·c++·设计模式·面试·c#·大学必学
jjjxxxhhh1232 小时前
c++设计模式之桥接模式
c++·设计模式·桥接模式
哭哭啼2 小时前
spring boot 常用参数总结
spring boot·后端·pycharm
凡人的AI工具箱2 小时前
15分钟学 Go 第 54 天 :项目总结与经验分享
开发语言·人工智能·后端·算法·golang