设计模式(1)—观察者模式

1、接口回调

在学习观察者模式之前,我们先了解一下接口回调的概念。两者的原理有些类似,理解了接口回调就非常容易理解观察者模式。

所谓接口回调一般应用的场合是:你不知道这个方法什么时候回返回,但是你希望在该方法结束的时候拿到方法执行的结果。常见的,比如一个方法内部开启了线程,而我们希望在线程执行结束的时候拿到线程的执行结果。

在下面的例子中,方法cal()定义了一个局部变量i,随后开启了一个线程,并在线程执行的时候修改了i的值。我们希望在线程执行完毕的时候达到i的执行结果。

public int cal() {
    int i;
    new Thread(new Runnable() { 
        Thread.sleep(3000); // throw ... ignore it
        i++; 
    }).start();
    return i;
}
复制代码

使用return肯定不行,因为方法结束时,线程可能还没有结束,那么return返回的结果是无法被预料到的,可能是线程执行完毕之后的,也可能是没有被线程修改就返回了的。

我们可以使用接口回调解决这个问题。我们可以在调用该方法的时候传入一个接口Callback的实例。在线程中执行完所有的逻辑之后,我们使用该接口的call()方法将i回调出来:

public void cal(Callback callback) {
    int i;
    new Thread(new Runnable() { 
        Thread.sleep(3000); // throw ... ignore it
        i++; 
        if (callback != null) {
            callback.call(i); // 1
        }
    }).start();
}
复制代码

当然,接口回调更像是将我的call()方法注入到了上述代码中的1处。这近似于所谓的函数编程的概念,就是将接口作为一个函数注入到了方法中。

接口回调有非常丰富的应用场景,典型的是一些异步的场景,比如“监听”一个按钮的执行结果等等。

2、观察者模式

好了,了解了上面的接口回调之后,我们来看下观察者模式。首先,我们来了解一下观察者设计模式中的一些概念。

如果使用1中的的例子来做类比的话,那么i就是我们的主题(Subject),我们进行接口回调的类(将实现的Callback传入到cal方法时所处的类)叫做观察者(Subscriber)

观察者模式UML

上面的是观察者模式的UML模型。这里的Subject接口就是主题的接口,而ConcreteSubkect是它的具体实现类,即具体的 主题。Observer接口是观察者的接口,ConcreteObserver是具体的观察者。一个主题往往会通过列表来维护一系列的观察者,然后当主题发生变化的时候会便利这个列表来通知所有的观察者。所以,这里的Subject接口定义了三个方法,从上到下依次用于向主题中添加观察者,从主题中移除观察者以及通知所有的观察者主题的更新。

下面我们给出一份最简单的观察者设计模式的代码:

1.首先是主题的定义类:

public class ConcreteSubkect implements Subject {

    // 通过队列维护观察者列表
    private List<Observer> observers;

    // 注册一个观察者
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    // 移除一个观察者
    @Override
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i >= 0) {
            observers.remove(o);
        } 
    }

    // 通知所有观察者主题的更新
    @Override
    public void notifyObservers() {
        for (Observer o : observers) {
            ((Observer) observers.get(i)).method();
        }
    }
}
复制代码

2.接下来是观察者的定义类:

public class ConcreteObserver implements Observer {
    
    // 该观察者订阅的主题
    private Subject subject;

    public ConcreteObserver(Subject subject) {
        this.subject = subject;
        // 将当前观察者添加到主题订阅列表中
        subject.registerObserver(this);
    }
    
    // 当主题发生变化的时候,主题会遍历观察者列表并通过调用该方法来通知观察者
    @Override
    public void method() {
        // ...  
    }
}
复制代码

上面就是观察者的基本的实现方式,这里的实现逻辑比较简单,但当你学习更加复杂的观察者设计的之前理解它是很有必要的。

3、总结

观察者与被观察者之间是属于轻度的关联关系,并且是抽象耦合的,这样,对于两者来说都比较容易进行扩展。

观察者模式是一种常用的触发机制,它形成一条触发链,依次对各个观察者的方法进行处理。但同时,这也算是观察者模式一个缺点,由于是链式触发,当观察者比较多的时候,性能问题是比较令人担忧的。并且,在链式结构中,比较容易出现循环引用的错误,造成系统假死。并且因为观察者列表中维护了一份观察者的引用,当它们没有被及时地释放的话,可能会引起内存泄漏。

猜你喜欢

转载自juejin.im/post/5b60659df265da0f793a85ba