观察者模式的概念:
观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式的涉及的角色:
-
抽象主题(Subject)角色(被观察者):抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。
-
具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。
-
抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。
-
具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。
举例:
今日,多地发生洪涝灾害,在暴雨季节,河流的堤坝成为了各方(武警、消防、医护)重点关注的对象。
在本案例中,堤坝成为了被观察者对象,是否出现管涌、决堤等情况,武警、消防、医护是观察者,根据堤坝的状态做出相应的措施。
代码实现:
抽象主题角色(被观察者)
public abstract class Subject {
private List<Observer> obvs = new ArrayList<>();
/**
* 添加观察者
*
* @param obv
*/
public void addObserver(Observer obv) {
obvs.add(obv);
}
/**
* 移除观察者
*
* @param obv
*/
public void removeObserver(Observer obv) {
obvs.remove(obv);
}
/**
* 通知所有观察者
*
* @param state
*/
public void notice(Integer state) {
for (Observer obv : obvs) {
obv.takeAction(state);
}
}
}
具体角色者:
public class RiverSubject extends Subject {
private Integer state;
/**
* 状态改变
*
* @param state
*/
public void changeState(Integer state) {
System.out.println("河流-主题 (被观察者)状态发送改变.....");
this.state = state;
//通知所有观察者
notice(state);
}
}
抽象观察者:
public interface Observer {
/**
* 采取措施
*
* @param state
*/
void takeAction(Integer state);
}
具体观察者(武警):
public class ArmPoliceObserver implements Observer {
@Override
public void takeAction(Integer state) {
switch (state) {
case 1:
System.out.println("武警官兵-----协助人工围堵-----");
break;
case 2:
System.out.println("武警官兵-----协助使用机械围堵-----");
break;
default:
System.out.println("武警官兵-----全力围堵-----");
}
}
}
具体观察者(消防):
public class FirefightingObserver implements Observer {
@Override
public void takeAction(Integer state) {
switch (state) {
case 1:
System.out.println("消防官兵-----人工围堵-----");
break;
case 2:
System.out.println("消防官兵-----使用挖掘机围堵-----");
break;
default:
System.out.println("消防官兵-----卡车装石头围堵-----");
}
}
}
具体观察者(医护):
public class HealthCareObserver implements Observer{
@Override
public void takeAction(Integer state) {
switch (state) {
case 1:
System.out.println("医护人员-----一级准备 是否有人员受伤-----");
break;
case 2:
System.out.println("医护人员-----二级准备 是否有人员受伤-----");
break;
default:
System.out.println("医护人员-----三级准备 时刻准备抢救-----");
}
}
}
客户端:
public class TestFor {
public static void main(String[] args) {
Subject subject = new RiverSubject();
Observer obv1 = new ArmPoliceObserver();
Observer obv2 = new FirefightingObserver();
Observer obv3 = new HealthCareObserver();
subject.addObserver(obv1);
subject.addObserver(obv2);
subject.addObserver(obv3);
// 1 2 3 分别代表堤坝的不同状态
subject.notice(1);
System.out.println("-----------------------------------");
subject.notice(2);
System.out.println("-----------------------------------");
subject.notice(3);
}
}
运行结果:
武警官兵-----协助人工围堵-----
消防官兵-----人工围堵-----
医护人员-----一级准备 是否有人员受伤-----
-----------------------------------
武警官兵-----协助使用机械围堵-----
消防官兵-----使用挖掘机围堵-----
医护人员-----二级准备 是否有人员受伤-----
-----------------------------------
武警官兵-----全力围堵-----
消防官兵-----卡车装石头围堵-----
医护人员-----三级准备 时刻准备抢救-----
观察者模式使用的场景:
(1)当一个对象的数据更新时需要通知其他对象,但这个对象又不希望和被通知的那些对象形成紧耦合。
(2)当一个对象的数据更新时,这个对象需要让其他对象也各自更新自己的数据,但这个对象不知道具体有多少对象需要更新数据。
观察者模式的优缺点:
优点:
1、具体主题和具体观察者是松耦合关系。由于主题接口仅仅依赖于观察者接口,因此具体主题只是知道它的观察者是实现观察者接口的某个类的实例,但不需要知道具体是哪个类。同样,由于观察者仅仅依赖于主题接口,因此具体观察者只是知道它依赖的主题是实现主题接口的某个类的实例,但不需要知道具体是哪个类。
2、观察者模式满足“开-闭原则”。主题接口仅仅依赖于观察者接口,这样,就可以让创建具体主题的类也仅仅是依赖于观察者接口,因此,如果增加新的实现观察者接口的类,不必修改创建具体主题的类的代码。。同样,创建具体观察者的类仅仅依赖于主题接口,如果增加新的实现主题接口的类,也不必修改创建具体观察者类的代码。
缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。