观察者模式:定义对象之间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,其他相关依赖对象都得到通知并被自动更新。
观察者模式是使用频率较高的一个模式,它建立了对象与对象之间的依赖关系,当一个对象发生了改变,自动会通知其他对象。发生改变的对象被称为观察目标,被通知的对象被称为观察者。
当我们看到绿灯就会通过,看到红灯就会停止行走,红灯就是观察目标,我们就是观察者,红灯只有一个,而我们是一群人,也就是说一个目标可以对应多个观察者。
观察者模式的类图如下:
Subject(目标):目标也称为主题,也就是被观察的对象。我们可以在目标中定义一个观察者集合,它提供方法来增加或者删除观察者对象,同时它最主要的方法是通知方法notify,可以通知观察者。
ConcreteSubject(具体目标):它是目标的子类,当它的状态发生改变时,主要是用来向各个观察者发送通知。
Observer(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口。
ConcreteObserver(具体观察者):它是观察者的子类,在具体观察者中维护了一个指向具体目标的引用。
在某多人联机对战游戏中,多个玩家可以加入同一战队组成联盟,当战队中的一人遭到敌人攻击时将给所有的其他盟友发送通知,盟友收到通知后将做出反应。
如果不用设计模式,正常的链路是这样的:联盟成员遭到攻击---->通知给盟友---->盟友做出反应,这样的弊端是如果盟友有很多,则每一个成员都需要进行关联,耦合性太严重。加入观察模式的话,以指挥部作为一个新的对象,链路变成这样:联盟成员遭到攻击---->通知指挥部---->指挥部通知所有盟友---->盟友做出反应。
AllyControlCenter,指挥部中心,充当抽象目标类
@Data
public abstract class AllyControlCenter {
protected String allyName;
protected ArrayList<Observer> players = new ArrayList<>();
public void join (Observer obs){
System.out.println(obs.getName()+ "加入"+ this.allyName + "战队");
players.add(obs);
}
public void quit(Observer obs){
System.out.println(obs.getName()+ "退出"+ this.allyName + "战队");
players.remove(obs);
}
public abstract void notifyObserver(String name);
}
ConcreteAllyControlCenter类,充当具体目标类
@Data
public class ConcreteAllyControlCenter extends AllyControlCenter {
public ConcreteAllyControlCenter(String allyName) {
System.out.println(allyName + "战队组建成功");
this.allyName = allyName;
}
@Override
public void notifyObserver(String name) {
System.out.println(this.allyName + "战队紧急通知,盟友" + name + "遭到敌人攻击");
for (Observer player : players) {
if (!name.equals(player.getName())) {
player.help();
}
}
}
}
Observer,抽象观察者
public interface Observer {
String getName();
void setName(String name);
void help();
void beAttacked(AllyControlCenter acc);
}
Player,具体观察者
public class Player implements Observer {
private String name;
public Player(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void help() {
System.out.println("坚持住" + this.name + "来救你");
}
@Override
public void beAttacked(AllyControlCenter acc) {
System.out.println(this.name + "被攻击");
acc.notifyObserver(name);
}
}
客户端
public class Client {
public static void main(String[] args) {
AllyControlCenter allyControlCenter;
allyControlCenter = new ConcreteAllyControlCenter("联盟");
Observer play1, play2, play3;
play1 = new Player("play1");
allyControlCenter.join(play1);
play2 = new Player("play2");
allyControlCenter.join(play2);
play3 = new Player("play3");
allyControlCenter.join(play3);
play1.beAttacked(allyControlCenter);
}
}
打印结果:
具体调用流程:Player.beAttacked()—> AllyControlCenter.notifyObserver()—>Player.help()
另外,jdk 的util 包中自带观察者模式,我们可以直接继承和实现这两个类,使用起来更加方便。
观察者模式的优点:
1、表示层和数据逻辑层分离,并抽象了更新的接口,便于不同的表示层充当观察者角色。
2、在观察目标和观察者之间建立了一个抽象的耦合。观察者目标只需要维护一个抽象的观察者集合即可,无需了解具体观察者。
3、简化了一对多系统的难度,支持广播通信。
4、符合开闭原则,增加新的观察者无需修改原代码。
观察者模式的缺点:
1、观察者太多的话,有性能问题。
2、如果观察者和观察目标之间存在循环依赖,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,仅仅是知道目标发生了变化。
适用环境:
1、当一个抽象模型的一方面依赖另一方面时,可以考虑观察者模式。
2、一个对象的改变导致多个对象发生变动时。
3、需要在系统中新建一个触发链,A对象变动影响B对象,B对象变动影响C对象。