设计模式之观察者模式
观察者模式行为型设计模式,定义了对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并且自动更新。观察者模式也被称作发布-订阅模式。现实中,最简单的例子就是红绿灯,在十字路口的交通信号灯,红灯亮则汽车停,绿灯亮则汽车行。
观察者模式结构图
观察这模式包含Subject(目标),ConcreteSubject(具体目标),Observer(观察者)和ConcreteObserver(具体观察者)。
Subject(目标):指的时被观察的对象。在它里面定义了一个观察者集合,可存储任意数量的观察者对象,提供了增加和删除观察者对象的接口,还有一个notify()
的通知方法。
ConcreteSubject(具体目标):具体目标是目标类的子类,通常它包含经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知。
Observer(观察者):观察者是对目标的改变做出反应的对象,观察者一般定义为接口,声明观察者行为的接口,例如update()
。
ConcreteObserver(具体观察者):具体观察者中维护了一个指向具体目标对象的引用,它主要用于具体观察者的有关状态,这些状态需要和具体目标的状态保持一致。
观察者模式实例
现在有一个场景,在大学校园里,有一个班级正在上课,上的课是编译原理,因为课程比较难,且枯燥,班里的同学们有的在玩手机,有的在开小差,有的在睡觉,但是上课的过程中,老师经常会课堂上点名提问学生问题,答不出来会有惩罚。所以一旦提问,无论正在干什么的同学,都会立即提高警觉,相互讨论,进入紧张的状态,等待老师的点名。在这场景中,学生们作为实际的观察者,老师是具体的目标,当老师提问这一个行为发生时,学生们的状态需要跟着更新,进入准备回答问题的状态。
Subject(目标),定义了一个观察者集合observers
,声明的通知方法是askAQuestion()
:
/*抽象目标类,BaseSubject*/
public abstract class BaseSubject {
protected List<IBaseObserver> observers = new ArrayList<>();
//注册
public void attachObserver(IBaseObserver observer){
observers.add(observer);
}
//注销
public void detachObserver(IBaseObserver observer){
observers.remove(observer);
}
/*抽象的通知方法*/
public abstract void askAQuestion();
}
ConcreteSubject(具体目标),在这个例子中,老师就是具体被观察的目标,实现通知方法是askAQuestion()
:
/*具体的目标实现类,讲课的老师*/
public class TeacherSubject extends BaseSubject{
@Override
public void askAQuestion() {
System.out.println("老师提问:为什么要学习编译原理?");
System.out.println("===========================================================");
//通知调用
for (IBaseObserver observer : observers){
observer.response();
}
}
}
Observer(观察者),是一个接口,里面定义了response()
方法,用作对目标对象的行为变化后,做出相应反应:
/*抽象的观察者接口*/
public interface IBaseObserver {
/*抽象的响应方法*/
void response();
}
ConcreteObserver(具体观察者),实现了Observer(观察者)里面定义了response()
方法,在这个例子中,听课的学生都是观察者,对于听课的学生,这里进行了分类,分为三类:认真听课的学生、打盹的学生和玩手机的学生,他们听到老师提问做出的反应是不一样的:
/*具体观察者:认真听课的同学,镇定自若*/
public class AttentiveStudent implements IBaseObserver {
@Override
public void response() {
System.out.println("认真听课的同学,镇定自若...");
}
}
/*具体观察者:在打盹的同学,听到提问做出反应*/
public class NapStudent implements IBaseObserver {
@Override
public void response() {
System.out.println("正在打盹的同学,忽然精神了,开始翻开书本查找答案...");
}
}
/*具体观察者:在玩手机的同学,听到提问做出反应*/
public class PlayPhoneStudent implements IBaseObserver {
@Override
public void response() {
System.out.println("正在玩手机的同学,放下手机,开始翻开书本查找答案...");
}
}
测试类:
/*客户端测试类*/
public class ClientDemo {
public static void main(String[] args) {
//老师是具体的被观察的对象
BaseSubject subject = new TeacherSubject();
IBaseObserver observerA;
IBaseObserver observerB;
IBaseObserver observerC;
observerA = new PlayPhoneStudent();
observerB = new NapStudent();
observerC = new AttentiveStudent();
//注册观察者到目标对象中
subject.attachObserver(observerA);
subject.attachObserver(observerB);
subject.attachObserver(observerC);
// 目标对象触发了部分事件或者行为,然后观察者做出反应
subject.askAQuestion();
}
}
测试程序运行结果:
观察者模式在什么场景下使用
一个对象的改变将导致其他一个或者多个对象也发生改变,而不知道具体有多少个对象将发生改变,使用观察者模式可以降低对象之间的耦合度;一个对象必须通知其他对象,而并不知道这些对象是谁。
观察者模式优缺点
优点
- 实现表示层和数据逻辑层的分离,并且定义了稳定的消息更新传递机制,数据逻辑层如上例中的老师提问,表示层就是学生们的反应,不同的学生会做出不同的行为反应表示。
- 符合“开闭原则”的要求,增加新的具体观察者无须修改原有系统代码。
缺点
- 因为是广播的通知方式,当不需要全部通知时,这种方式会所有都通知,浪费时间。
- 观察者模式没相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而只是知道目标对象发生了变化。对于这个问题,我觉得可以通过通知方法传参的方式将变化的状态传递过去,不知道有有没问题。