观察者模式主要用于当一个类对象改变时,多个对象做出相应的反应。例如,Android广播使用的就时观察者模式。当一些对象接受到消息,做出一系列的反应。
现在,我们模拟一个观察者的使用场景。
那是长征期间,红军战士为了躲避老蒋的追击,每到一处都会有警卫站岗,一旦警卫发现敌情就会通知他的战友们准备战斗,当他的战友得到通知时就会做出相应的行动。
在这里,警卫是通知者,他的战友们就是观察者, 如何实现呢?看下面的代码
定义一个红军类
//红军类
public class RedArmy {
private String name ;
public RedArmy(String name){
this.name = name ;
}
//进行相关更新操作
public void update(){
System.out.println("我是"+name+",我接收到消息了。");
}
public String getName(){
return this.name ;
}
}
定义一个警卫类
//警卫类
public class Guard {
private List<RedArmy> redArmies ;
public Guard(){
redArmies = new ArrayList<>();
}
//增加一个将要通知的战友
public void addRedArmy(RedArmy redArmy){
this.redArmies.add(redArmy);
}
//如果一个战友牺牲了,就不再通知他了
public void removeArmy(RedArmy redArmy){
this.redArmies.remove(redArmy);
}
public void notifyArmy(){
for (RedArmy redArmy : redArmies) {
redArmy.update(); //进行相应的操作
}
}
}
测试类
public class Test {
public static void main(String[] args) {
RedArmy r1 = new RedArmy("红军战士1"); //实例化一个红军战士
RedArmy r2 = new RedArmy("红军战士2");//实例化一个红军战士
Guard guard = new Guard(); //实例化一个警卫
guard.addRedArmy(r1); //将红军战士1作为警卫的通知对象
guard.addRedArmy(r2); //将红军战士2作为警卫的通知对象
guard.notifyArmy(); //通知红军战士
}
}
运行结果
我是红军战士1,我接收到消息了。 我是红军战士2,我接收到消息了。
从程序运行上,已经完全达到我们预期的效果,也满足我们的要求。但是,此时如果警卫不在,让侦察兵来通知,接下来我们该怎么做呢?对于码农的我们,抡起键盘就是再写一个和警卫类似的一个类。那么如果再换人呢,还要再写吗?此时我们应该想到面向对象中的抽象的特性了。我们可以抽象出一个通知类,可以进行通知。无论是警卫还是侦察兵,只要实现了抽象就可以了。其实这就是所谓的高大上的依赖抽象而不依赖具体。
定义一个抽象通知者类
//抽象的通知者
public abstract class Subject {
protected List<RedArmy> redArmys;
public Subject() {
this.redArmys = new ArrayList<>();
}
public void addRedArmy(RedArmy redArmy){
this.redArmys.add(redArmy);
}
public void removeRedArmy(RedArmy redArmy){
this.redArmys.remove(redArmy);
}
public void notifyGuard(){
for (RedArmy redArmy:redArmys) {
redArmy.update();
}
}
}
修改警卫类,继承抽象通知者类
//警卫类
public class Guard extends Subject{
}
测试类
public class Test {
public static void main(String[] args) {
RedArmy r1 = new RedArmy("红军战士1"); //实例化一个红军战士
RedArmy r2 = new RedArmy("红军战士2");//实例化一个红军战士
//这里直接sj = new Guard();如果换成侦察兵,只需改动sj = new 侦察兵();就可以了。
Subject sj = new Guard(); ////实例化一个警卫
sj.addRedArmy(r1); //将红军战士1作为警卫的通知对象
sj.addRedArmy(r2); //将红军战士2作为警卫的通知对象
sj.notifyArmy(); //通知红军战士
}
}
通过我们的第一步修改已经完成了通知者依赖抽象,现在看看代码还有什么问题了吗?没错,还是存在着依赖具体的缺陷。假设现在追击的不是老蒋而是小日本了,小日本可是非常毒的,他们不仅杀害红军战士,还伤害老百姓,所以此时警卫还要通知老百姓。代码怎么改呢?难道我们在通知者中再加一个List<老百姓>的集合吗?确实是可以,但是如果还要再去通知民兵呢?还要再修改通知者吗,这已经违背了设计模式的开放-封闭原则(对扩展开放,对修改关闭)我们尽量不修改就不去修改。
分析一下,无论是红军战士还是老百姓,还是民兵他们在这都是观察者,就是当他们一旦得到消息就会行动。所以我们抽象出一个观察者类。
抽象观察者类
//抽象观察者类
public abstract class Observer {
protected String name ;
public Observer(String name){
this.name = name ;
}
public abstract void update();
}
修改红军战士类,继承抽象观察者类即可。
//红军类
public class RedArmy extends Observer{
public RedArmy(String name) {
super(name);
}
@Override
public void update() {
System.out.println("我是"+name+",我需要开始战斗了。");
}
}
添加老百姓类
//老百姓类
public class Civilian extends Observer {
public Civilian(String name) {
super(name);
}
@Override
public void update() {
System.out.println("我是"+name+",我要转移了。");
}
}
添加民兵类
//民兵类
public class Militia extends Observer{
public Militia(String name) {
super(name);
}
@Override
public void update() {
System.out.println("我是"+name+",我要协助红军战士们参加战斗了");
}
}
最后再修改通知者类,让通知者依赖观察者的抽象,而不是具体实现
//抽象的通知者
public abstract class Subject {
//依赖观察者的抽象
protected List<Observer> observers ;
public Subject() {
this.observers = new ArrayList<>();
}
public void addObserver(Observer observer){
this.observers.add(observer);
}
public void removeObserver(Observer observer){
this.observers.remove(observer);
}
public void notifyArmy(){
for (Observer observer:observers) {
observer.update();
}
}
}
测试类
public class Test {
public static void main(String[] args) {
Observer r1 = new RedArmy("红军战士1"); //实例化一个红军战士
Observer r2 = new RedArmy("红军战士2");//实例化一个红军战士
Observer m1 = new Militia("民兵战士"); //实例化一个民兵战士
Observer c1 = new Civilian("老百姓"); //实例化一个老百姓
Subject sj = new Guard(); ////实例化一个警卫
sj.addObserver(r1); //将红军战士1作为警卫的通知对象
sj.addObserver(r2); //将红军战士2作为警卫的通知对象
sj.addObserver(m1);//将民兵战士作为警卫的通知对象
sj.addObserver(c1);//将老百姓作为警卫的通知对象
sj.notifyArmy(); //通知观察者
}
}
运行结果
我是红军战士1,我需要开始战斗了。 我是红军战士2,我需要开始战斗了。 我是民兵战士,我要协助红军战士们参加战斗了 我是老百姓,我要转移了。
我们可以看到完全可以实现我们的功能,当警卫发现出现敌情时,可以同时通知老百姓,红军战士,民兵,而他们由于身份不同,责任不同,所以做出了不同的选择。
现在即使就是侦察兵和警卫都不在了,我们只需要有一个类继承了通知者就完全可以取代警卫的工作。