详解设计模式——观察者模式

目录

情景介绍

观察者模式分析

Java实现

应用场景


 

观察者模式:就是多个对象之间存在一对多的关系,当目标对象状态发生改变时会通知多个观察者对象作出对应改变。

 

情景介绍

在以前(传统模式),学生上学、大人上班之前都会观察天气预报,以确定出门是否要带雨伞。在这种情况下,因为天气随时都可能发生改变,所以学生或大人需要每隔一段时间去查看天气。这显然会消耗大量的精力,是十分不合理的。

现在(观察者模式),天气局拥有每个人的信息,只要天气出现变化,就可以自动的通知所有人天气情况,让所有人作出对应的应对策略。这样,不需要学生和大人实时的看天气预报,大大的提高的消息的时效性。

在上面两个场景中,解释了观察者模式大致的运行流程,结合场景理解得到下图:

开头说,观察者模式是一对多的关系,其中可以把对象分为两个部分:Subject对象和Observer对象。

Subject对象即目标对象,相当于场景中的天气局,在观察者模式中有且仅有一个目标对象。其负责管理所有的Observer对象,当Subject中的某个状态改变时,就会触发方法对所有Observer对象进行通知。

Observer对象即观察者对象,相当于场景中的学生、大人……,都统一交由Subject对象管理,在观察者模式中可以包含多个观察者对象。当收到subject发来的通知时,Observer对象就会调用各自的方法来应对。

 

观察者模式分析

了解了观察者模式中的两大对象:Subject和Observer。就可以深入理解下这两个对象所包含的方法属性即实现原理。

对于Subject对象,首先需要一个存储Observer对象的列表,负责存储所有的Observer对象。回想场景,应该有新的人想要得到天气通知,也应该有人不想要得到天气通知。所以Subject应该有添加和删除方法来维护Observer列表。最后,还有最重要的是通知方法。

对于Observer对象,只需要一个得到通知后的应对方法就行。

那么,我们就可以得到如下结构:

其流程是这样的,Subject对象可以随意的添加或删除Object对象。当Subject中状态发生改变时,会自动调用Notify()方法。该方法会对Observer列表中所有Observer对象调用其各自的Update()方法来应对。

Subject对象的实现如下:

public class Subject {
    //Observer列表
    private List<Observer> observerList = new ArrayList<>();
    
    //添加Observer对象
    public void attach(Observer o) {
        observerList.add(o);
    }
    //删除Observer对象
    public void detach(Observer o) {
        observerList.remove(o);
    }
    //通知
    public void notifyObserver() {
        for (Observer o : observerList) {
            o.update();
        }
    }
}

Observer对象的实现如下:

public class Observer {
    
    public void update(){
        //do something
    }
}

上述实现只是在分析了场景后逻辑上的实现。但仔细观察会发现很多问题。首先List中存放的应该是统一的对象,所以对于不用类型的Observer应该抽象出一个接口,不同的Observer只需实现其不同update()方法。类似的,对于Subject来说,也同样可以抽象出一个接口,供交通局等其他地方使用。

于是,就有了完整的构造者模式的UML图:

分别抽象出Subject和Observer接口,将耦合变为了抽象耦合,大大减少了原来的耦合程度。 

 

Java实现

在Java中在util包中,官方提供给了内置的观察者模式,分别为Observable类和Observer接口。

Observer类相当于Subject接口,其源码如下:

public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    public Observable() {
        obs = new Vector<>();
    }

    //添加Observer
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    //删除Observer
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    //通知Observer
    public void notifyObservers(Object arg) {
        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    //状态发生改变
    protected synchronized void setChanged() {
        changed = true;
    }

}

相对于之前的Subject接口多了一个setChanged()方法,意为表明目标状态发生了改变,此方法必须在notifyObservers()之前调用。通过继承Observable来实现一下天气局的类:

public class weatherDate extends Observable {

    private String state = "晴天";

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
        weatherChanged();
    }

    public void weatherChanged(){
        setChanged();
        notifyObservers(state);
    }
}

这时一个简单的天气局实现类,当前默认的天气状态为晴天,通过setState()方法改变天气状态会自动通知所有Observer。

再来看看看官方提供的Observer接口,其源码如下:

public interface Observer {
    
    void update(Observable o, Object arg);
}

其中只有一个update()方法,但其中有两个参数,一个是Subject对象(天气局对象),一个是Subject对象传来的数据。那么这两个参数是什么意思呢?

这里就要涉及到两个模型:推模型和拉模型。

所谓的推模型就是当天气局状态改变时,通知所有Observer时会将一个参数传给Observer供其使用。例如天气从晴天转变为下雨时,可以通过推模型将降雨量这个参数绑定在notifyObservers()方法参数中,传给每个Observer,Observer通过update()接收到这个arg参数时会通过每个人不同的判断决定是否带伞。

而拉模型正好相反,当天气局状态改变时,通知所有Observer时会将自己这个天气局对象作为参数传给Observer,Observer获得了Subject对象就可以自由的通过其get()方法来获取里面的所有信息。

所以官方提供在update()方法中提供两个参数,同时支持推模型和拉模型,用户可以根据需求自主选择。

接着,可以看一下Observer接口的具体实现,其中以学生类为例:

public class student implements Observer {

    private String name = "张三";

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("今天" + arg + "," + name + "应该...");
    }
}

这里使用了其中的推模型,获取Subject对象中的天气状态。

最后在主函数中进行测试:

public static void main(String[] args) {
        weatherDate weather = new weatherDate();
        student zhangsan = new student();
        weather.addObserver(zhangsan);
        weather.setState("下雨");
    }

 

应用场景

观察者模式在Java中被广泛应用,其中较为经典的就是事件监听机制,其本质就是观察者模式。组件作为目标对象,不同的触发事件作为观察者对象,当组件被触发时,自动在观察者中判断属于哪个事件,然后事件作出相应策略。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值