设计模式之观察者模式

观察者模式是使用最为频繁的设计模式之一。在很多地方都有用到。比如各种编程语言的GUI事件处理实现,各种框架的实现,比如说EventBus、Rxjava以及MVC等等。

一、初次相识

先举个小栗子来了解一下观察者是干啥的~~

当我们在打团队游戏时,当你受到攻击需要队友帮忙时该怎么办?

这时候就需要给你所有的队友发送一条你正在被攻击的消息。所有的队友会根据你发送的消息作出相应的动作。比如有团队意识来帮你,或者不帮你继续玩自己的。

这里面的队员就是该设计模式名字中的观察者。那么受到攻击的自己的是什么呢。被观察者?不,准确的我们称之为目标或者主题。

所以整个流程大概就是:当目标(主题)的状态发送改变时就会通知观察者,观察者根据自己的情况做出相应的动作。

二、进步熟悉

通过上面我们大概对观察者的作用有了浅层的认识,这对接下来的学习很有帮助。

1.首先来看一下观察者模式(Observe Pattern)的正经定义:定义对象之间的一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。

它还有几个别名:发布-订阅模式、模型-视图模式、源-监听器模式以及从属者模式。比如我们看这个发布-订阅模式就很好理解,当订阅者订阅了某系列杂志,当杂志有了新的状态,比如更新了,那么此时就会给所有的订阅者发送一条消息,那么所有的订阅者就会收到此消息做出购买或不购买的选择。

2.那么它如何实现这么神奇的功能的呢?

先来看一下它的结构图


设计模式之观察者模式

从图中我们可以看到之前说的目标和观察者。只不过这里的目标和观察者都被抽象化了。我们来逐一认识:

(1)Subject(目标或主题)

它是指被观察的对象。我们在主题中定义一个观察者集合。一个观察者对象可以接收任意多个观察者。同时提供了一系列的方法管理这些观察者。

比如attach添加观察者到集合中,detach从集合中剔除观察者。notify通知集合中的所有观察者。

(2)ConcreteSubject(具体目标)

它拥有自己的状态,当它的状态的改变时就会通知各个观察者。

同时还实现了在目标类中定义的抽象逻辑方法(如果有的话)

(3)Observer(抽象观察者)

它是一个接口,观察者将对观察目标状态的改变做出相应的反应

该接口定义了更新数据的方法update

(4)ConcreteObserver(具体观察者)

具体观察者中会维护一个指向具体目标对象的引用,它存储了具体观察者的状态,这些状态和具体目标的状态要保持一致。

它实现了抽象观察者对象的updata方法。

通常在实现时,可以调用具体目标的attach和detach方法将自己加入到集合中或者从集合中剔除。

3.接下来通过一个代码实例来加深对上面的结构图的理解

目标抽象类 Subject.java

public abstract class Subject {     

protected ArrayList observers=new ArrayList<>();     

//把观察者对象添加到观察者集合中     

public void attach(Observer observer) {           

observers.add(observer);     

}     

//把观察者对象剔除到观察者集合中     

public void detach(Observer observer) {           

observers.remove(observer);     

}                         

//声明抽象方法     

public abstract void notifyObserver();}

具体目标类 ConcreteSubject.java 继承抽象类并实现通知观察者的方法

public class ConcreteSubject extends Subject{     

//实现通知的方法     

@Override     

public void notifyObserver() {           

System.out.println("目标对象状态已变化......发送通知给观察者中");           

for(Object object:observers){                   

((Observer)object).update();           

}                 

}}

观察者接口 Observer 定义一个更新的方法

//观察者接口

public interface Observer {     

public void update();

}

具体的观察者 ConcreteSubject.java

public class ConcreteObserver implements Observer {     

private String observerName;     

public ConcreteObserver(String observerName) {           

this.observerName=observerName;     

}     

@Override     

public void update() {           

System.out.println(observerName+"我要更新一下我的状态了......");      }

}

这里再添加一个具体的观察者,测试是否能够同时收到消息并更新状态ConcreteOberverOther.java

public class ConcreteOberverOther implements Observer{     

private String observerName;             

public ConcreteOberverOther(String observerName) {           

this.observerName=observerName;     

}             

@Override     

public void update() {           

System.out.println(observerName+"我要更新一下我的状态了......");     

}

}

最后准备一个类测试一下:

public class NotifyMain {     

public static void main(String[] args) {           

Subject subject=new ConcreteSubject();           

Observer observer=new ConcreteObserver("观察者一号");         

  Observer observer2=new ConcreteOberverOther("观察者二号");           

subject.attach(observer);           

subject.attach(observer2);           

subject.notifyObserver();     

}

}

将两个观察者通过attach添加到观察者集合中,然后由目标来发送一条更新的消息。观察者接收到消息后做出反应。运行结果如下:


设计模式之观察者模式

三、总结告别

对于上面的分析,我么对观察者设计模式就有了一个相对比较清晰的认识。但要对它能够熟练的使用,还需要一定的时间练习。

那么最后我们来总结一下它的优缺点,让我们能更好的使用它。

1.主要优点

(1)观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息传递机制,并抽象了更新接口,使得可以有各种各样的表示层充当具体的观察者角色。

(2)观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察者对象只需要维持一个抽象观察者的集合,无需了解其具体观察者。

(3)观察者模式支持广播通信,观察目标会向所有已注册的观察者发送通知,降低了一对多系统的设计难度。

(4)观察者模式满足开闭原则的要求,增加新的具体观察者无须修改原有的系统代码。

2.主要缺点

(1)如果一个观察目标对象有很多的直接观察者和间接观察者,那么所有的观察者接收到消息会耗费大量的时间。

(2)如果观察者和被观察者之间存在循环依赖,那么观察目标会触发它们之间进行循环调用,可能导致系统崩溃。

(3)观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道目标观察对象发生了变化。

设计模式是软件开发人员的内功,要想成为绝世高手,深厚的内功功底必不可少。让我们一起来修炼这内功,让它成为我们成长路上的垫脚石吧。