书接上回,本篇讲一下行为型模式-观察者模式
观察者模式
定义:定义了对象之间的一对多依赖关系,让多个依赖对象(观察者)同时监听某一个主题对象(被观察者/目标对象),当主题对象状态发生变化时,它的所有依赖对象都会收到通知并自动更新。
UML图
Subject:目标对象,也叫被观察对象,观察者模式中属于一方。它一般有以下几个功能
1> 一个目标可以别多个观察者观察
2>目标提供对观察者添加与删除的维护
3>当状态发送变化时,需要通知所有观察者。
/**
* 目标对象(被观察者)
*/
public class Subject {
//观察者(订阅者)集合
private List<Observer> observerList = new ArrayList<>();
//添加观察者(订阅者)
public void attach(Observer observer){
observerList.add(observer);
}
//删除观察者(订阅者)
public void detach(Observer observer){
observerList.remove(observer);
}
//通知所有观察者(订阅者),并回调它们暴露update方法,实现同步更新
public void notifyObservers(){
for (Observer observer : observerList) {
observer.update(this);
}
}
}
Observer:一个接口,成为观察者,观察者模式中属于多方。它一般需要实现以下功能
1>定义一个update方法,方法参数为目标对象,当目标对象触发通知时,调用。
/**
* 观察者(订阅者)
*/
public interface Observer {
/**
* 当目标对象状态变化后,该方法会被调用,
* 目的:同步更新观察者(订阅者)状态
* @param subject
*/
void update(Subject subject);
}
ConcreteSubject:具体的目标实现对象,里面会维护目标状态,当状态改变时,通知所有观察者。
public class ConcreteSubject extends Subject{
private String state = "A状态";
public void changeState(String state){
System.out.println("目标对象state状态发生变动了,由原来:"+ this.state + ",改为:" + state);
this.state = state;
//状态变动后通知所有观察者(订阅者)
notifyObservers();
}
public String getState() {
return state;
}
}
ConcreteObserver:观察者的具体实现对象,用来接收目标的通知,并执行预设的业务逻辑。
//具体观察者(订阅者)
public class ConcreteObserver implements Observer{
@Override
public void update(Subject subject) {
System.out.println("观察到目标对象(被观察者:subject)的状态变化了....");
ConcreteSubject sb = (ConcreteSubject) subject;
System.out.println("最新状态为:" + sb.getState());
}
}
App:测试类
public class App {
public static void main(String[] args) {
//观察者(订阅者)
ConcreteObserver ob = new ConcreteObserver();
//目标对象(被观察者)
ConcreteSubject sb = new ConcreteSubject();
sb.attach(ob);
sb.changeState("B状态");
}
}
结果:
目标对象state状态发生变动了,由原来:A状态,改为:B状态
观察到目标对象(被观察者:subject)的状态变化了....
最新状态为:B状态
案例分析
需求:商品到货通知
描述:IPhone20 出来的,N多果粉想订购,应为太火了,供不应求,官网提供到货通知机制,小明跟小红关注了。希望到货之后能第一时间收到消息。
UML图
Subject:目标对象-商品
定义目标对象,包含3个方法
attach:添加所有观察者(订阅者)
detach:删除退订的观察者(订阅者)
notifyObservers:当状态变化时,通知所有观察者(订阅者)
/**
* 目标对象(被观察者):商品
*/
public class Subject {
//观察者(订阅者)集合
private List<Observer> observerList = new ArrayList<>();
//添加观察者(订阅者)
public void attach(Observer observer){
observerList.add(observer);
}
//删除观察者(订阅者)
public void detach(Observer observer){
observerList.remove(observer);
}
//通知所有观察者(订阅者),并回调它们暴露update方法,实现同步更新
public void notifyObservers(){
for (Observer observer : observerList) {
observer.update(this);
}
}
}
ProductObserver :具体商品
定义真实的目标对象,上面Subject类 定义目标对象基本操作,ProductObserver 扩展具体的状态变化逻辑。
package com.langfeiyes.pattern.observer.demo2;
//目标对象:商品
public class ProductObserver extends Subject{
private String name;
private boolean state = false; //约定:false:缺货,true:有货
public ProductObserver(String name){
this.name = name;
}
//进货
public void stock(int count){
System.out.println("商家最新进货,上架商品:" +this.name+ ",共" + count +" 部");
state = true;
notifyObservers();
}
public boolean getState(){
return state;
}
public String getName() {
return name;
}
}
Observer:观察者-消费者
定义观察者实现操作规则,只有接口方法。
/**
* 观察者(订阅者):消费者
*/
public interface Observer {
/**
* 当目标对象状态变化后,该方法会被调用,
* 目的:同步更新观察者(订阅者)状态
* @param subject
*/
void update(Subject subject);
}
ConsumerObserver:具体消费者
定义具体订阅者,实现update方法,update方法中定制当目标对象发生变化时,订阅者如何同步更新自身状态(具体的业务逻辑)
//观察者:消费者
public class ConsumerObserver implements Observer{
private String name;
public ConsumerObserver(String name){
this.name = name;
}
@Override
public void update(Subject subject) {
ProductObserver po = (ProductObserver) subject;
if(po.getState()){
System.out.println(this.name + ":心念念的"+po.getName()+",终于到啦,抢呀~");
}else{
System.out.println(this.name + "小明:还没到啊,花都谢了~");
}
}
}
App:测试类
public class App {
public static void main(String[] args) {
//目标对象
ProductObserver ob = new ProductObserver("Iphone20");
//观察者
ConsumerObserver xm = new ConsumerObserver("小明");
ConsumerObserver xh = new ConsumerObserver("小红");
//小明小红订阅:到货通知
ob.attach(xm);
ob.attach(xh);
//商品进货
ob.stock(100);
}
}
结果:
商家最新进货,上架商品:Iphone20,共100 部
小明:心念念的Iphone20,终于到啦,抢呀~
小红:心念念的Iphone20,终于到啦,抢呀~
适用场景
关联行为场景,建立一套触发机制
比如:各种事件触发,各种通知,各种推送,都可以使用观察者模式
优缺点
优点
观察者和被观察者之间建立抽象的耦合
观察者模式实现动态联动
观察者模式支持广播通信
缺点
可能会引起无畏的操作
观察者之间有过多的细节依赖,提高时间消耗及程序复杂度
使用要得当,要避免循环调用
实现方式
在实现观察模式实现中,具体分2种情况:推模型和拉模型
推模型
目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,推送的信息通常是目标对象的全部或部分数据。此为被动型
拉模型
目标对象在通知观察者信息时,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于观察者从目标对象中拉数据。此为主动型
开发案例
还是看回JDK,JDK中其实有个现成的观察模式
Observable :可观察的(即被观察者/目标对象)
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
public void notifyObservers() {
notifyObservers(null);
}
public void notifyObservers(Object arg) {
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
public synchronized void deleteObservers() {
obs.removeAllElements();
}
protected synchronized void setChanged() {
changed = true;
}
}
Observer : 观察者
public interface Observer {
void update(Observable o, Object arg);
}
代码跟上面我们写的逻辑一样,区别:就是JDK为线程安全的,使用Vector缓存观察者,使用synchronized 限制添加,删除,清空观察者,保证原子操作。
下面是使用JDK方式,重写商品订购案例, 只需要2个方法即可。
ConsumerObserver
//观察者:消费者
public class ConsumerObserver implements Observer {
private String name;
public ConsumerObserver(String name){
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
ProductObserver po = (ProductObserver) o;
if(po.hasChanged()){
System.out.println(this.name + ":心念念的"+po.getName()+",终于到啦,抢呀~");
}else{
System.out.println(this.name + "小明:还没到啊,花都谢了~");
}
}
}
ProductObserver
//目标对象:商品
public class ProductObserver extends Observable {
private String name;
public ProductObserver(String name){
this.name = name;
}
//进货
public void stock(int count){
System.out.println("商家最新进货,上架商品:" +this.name+ ",共" + count +" 部");
super.setChanged(); //改变状态
notifyObservers(); //发起通知
}
public String getName() {
return name;
}
}
总结
观察者模式的本质:触发联动
1>当目标对象状态变动时,触发通知,所有关注该对象的观察者都可以联动同步更新
2>观察者联动同步更新可以通过注册与取消来控制观察者
3>另外目标对象和观察者对象之间是解耦,可以保证观察者无论发送啥变化,目标对象都能联动起来