一、观察者模式
概述
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)
观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在观察者模式中,主体是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知。观察者模式不仅被广泛应用于软件界面元素之间的交互,在业务对象之间的交互、权限管理等方面也有广泛的应用
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知
观察者模式(Observer) 也叫做"发布-订阅模式"(Publish / Subscribe)
优缺点
优点:
1、观察者和被观察者是抽象耦合的
2、建立一套触发机制
缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化
1. 各个角色介绍
1.1 Observer (观察者)
Observer是1个抽象类,我们可以理解它是一个抽象观察者类
它只有1个update()方法,也就是说它可以通过这个方法来执行某些动作
1.2 Subject(通知者/被观察者)
Subject是一个抽象类,我们可以理解为一个抽象被观察者类
它有5个方法
-
attach() 和 detach() 方法用来增删观察者的数量,也就是指当前被观察者对象到底有多少个观察者在观察着它
-
setState() 和 getState() 用于获取和设置通知者对象本身的状态, 这个状态通常是传送给观察者们的信息或参数也就是说观察者模式到底在观察什么,无非就是观察被观察者的这个状态
-
notifyAllObservers(),被观察者通知所有观察者,让观察者根据自己的当前状态 getState() 执行自己的 update() 方法
1.3 ConcreteSubject (具体通知者/被观察者)
这个(些)就是具体的被观察者类,只要继承了Subject抽象类,就可以添加一些对象作为自己的观察者
通过以上讲解,可以体会到这个类里面肯定需要一个容器,用于存放观察者的对象,而这个容器可以根据需要由具体的被观察者选择,通常是无序不能重复的Set容器(例如Set集合)
而 notifyAllObservers() 方法无非就是遍历自己的观察者容器,逐个执行观察者的update()方法
1.4 ConcreteObserver(具体观察者类)
这个(些)就是具体的观察这类,但是它们都必须继承Observer抽象类
一旦这些对象被通知者加入自己的容器,就相当于观察者正在观察某个被观察者
注意,观察者可以被多个被观察者加入自己的容器,也就是相当于观察者观察了多个被观察者(但这就打破了观察者模式)
2. UML图
3. 具体例子和代码
角色分配
- AbstractSubject:被观察者抽象类
- AbstractObserver:观察者抽象类
- Commander:指挥官—被观察者
- ArcherObserver:弓箭手观察者
- ConnoneerObserver:炮手观察者
- CavalrymanObserver:骑兵观察者
3.1 抽象类
- AbstractSubject
package com.vinjcent.pattern.observer;/*** @author vinjcent* @description 被观察者抽象类*/
public abstract class AbstractSubject {/*** 被观察者状态*/protected int state;/*** 用于获取通知者对象本身的状态** @return 被观察者的状态变量*/public abstract int getState();/*** 用于设置通知者对象本身的状态** @param state 被观察者的状态变量*/public abstract void setState(int state);/*** 用于绑定观察者** @param observer 观察者对象*/public abstract void attach(AbstractObserver observer);/*** 接触绑定观察者** @param observer 观察者对象*/public abstract void detach(AbstractObserver observer);/*** 被观察者通知所有观察者,让观察者根据绑定的被观察者的当前状态getState()执行自己的update()方法*/public abstract void notifyAllObservers();
}
- AbstractObserver
package com.vinjcent.pattern.observer;/*** @author vinjcent* @description 观察者抽象类*/
public abstract class AbstractObserver {/*** 观察对象*/protected AbstractSubject subject;/*** 观察者观察之后的行为*/public abstract void update();
}
3.2 被观察者
- Commander
package com.vinjcent.pattern.observer;import java.util.HashSet;
import java.util.Set;/*** @author vinjcent* @description 指挥官---被观察者*/
public class Commander extends AbstractSubject {/*** 士兵集合*/private final Set<AbstractObserver> soldiers = new HashSet<>();@Overridepublic int getState() {return this.state;}@Overridepublic void setState(int state) {this.state = state;notifyAllObservers();}@Overridepublic void attach(AbstractObserver soldier) {this.soldiers.add(soldier);}@Overridepublic void detach(AbstractObserver soldier) {this.soldiers.remove(soldier);}@Overridepublic void notifyAllObservers() {// 通知所有观察者if (this.soldiers.isEmpty()) {return;}this.soldiers.forEach(AbstractObserver::update);}
}
3.3 观察者
- ArcherObserver
package com.vinjcent.pattern.observer;/*** @author vinjcent* @description 弓箭手观察者*/
public class ArcherObserver extends AbstractObserver {public ArcherObserver(AbstractSubject subject) {this.subject = subject;this.subject.attach(this);}@Overridepublic void update() {System.out.println("Archer String: "+ Integer.toBinaryString(subject.getState()));}}
- ConnoneerObserver
package com.vinjcent.pattern.observer;/*** @author vinjcent* @description 炮手观察者*/
public class ConnoneerObserver extends AbstractObserver {public ConnoneerObserver(AbstractSubject subject) {this.subject = subject;this.subject.attach(this);}@Overridepublic void update() {System.out.println("Connoneer String: "+ Integer.toBinaryString(subject.getState()));}
}
- CavalrymanObserver
package com.vinjcent.pattern.observer;/*** @author vinjcent* @description 骑兵观察者*/
public class CavalrymanObserver extends AbstractObserver {public CavalrymanObserver(AbstractSubject subject) {this.subject = subject;this.subject.attach(this);}@Overridepublic void update() {System.out.println("Cavalryman String: "+ Integer.toBinaryString(subject.getState()));}
}
3.4 测试主函数
package com.vinjcent.pattern.observer;/*** @author vinjcent* 观察者模式*/
public class Main {public static void main(String[] args) {Commander commander = new Commander();new ArcherObserver(commander);new CavalrymanObserver(commander);new ConnoneerObserver(commander);System.out.println("第一次改变: ");commander.setState(22);System.out.println("第二次改变: ");commander.setState(23);}}
- 测试结果
4. 使用场景
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度
- 一个对象必须通知其他对象,而并不知道这些对象是谁
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制
注意事项:
1、JAVA 中已经有了对观察者模式的支持类
2、避免循环引用
3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式