观察者模式 Observer 发布订阅模式 源 监听 行为型 设计模式(二十三)

观察者模式 Observer
image_5c1af1b9_5671

意图

定义对象一种一对多的依赖关系,当一个对象的状态发生改变时,全部依赖他的对象都获得通知并自动更新。
别名:依赖(Dependents),发布订阅(Publish-Subscribe)源-监听(Source-Listener)
 
image_5c1af1b9_431a
《Hold On, We're Going Home》是加拿大说唱歌手德雷克与制做组合Majid Jordan合做的节奏布鲁斯歌曲
第一句“I got my eyes on you”就是“我一直关注你”
 
I got my eyes on you,
You're everything that I see
I want your hot love and emotion, endlessly
I can't get over you,
You left your mark on me......
 
电视剧中,也是常常出现”观察、监视“,好比《黎明以前》
是刘江执导2010年出品的谍战剧,由吴秀波、林永健、陆剑民、海清等领衔主演。豆瓣评分高达9.2。
有一段周汉亭被盯梢的桥段,有负责门口监视的,有负责电话汇报状况的,有负责指挥的....他的一举一动都在敌人的监视以内,引起无数个探子连锁行动。
 
歌词中由于喜欢妹子因此持续关注,妹子是目标,歌手是观察者。
电视剧中敌人为了破坏我党工做,因此监视,周汉亭是目标,众多探子是观察者。
经过对目标的观察,观察者能够得到事物的动向状况,进而作出进一步的行动。这就是观察。
 
在程序中,也常常会出现这种场景
一个系统中必然由多个相互协做的类组成,很常见的问题就是维持状态的一致性或者说联动效果
观察者模式就是为了解决这种问题,处理“协做者”之间的一致性与联动问题
 
好比,数据库中对某个字段进行了更新,可能须要同步修改其余字段
 
好比,执行一个main方法,IDE的窗口的联动效果,以下图所示,点击run执行后
底部状态栏会显示Build状态,很快完成后就开始运行,侧边栏显示运行状态,而后控制台打印输出信息
这是一系列的联动效果
image_5c1af1b9_6655
好比,同一份数据有多种图表展现,若是数据发生变化,每一个图表都须要发生变化
image_5c1af1b9_1bae

结构

假设目标为Subject ,观察者为Observer(一个或者多个)
最简单的实现方式,就是Subject直接调用Observer的方法。
image_5c1af1b9_4e72
当事件发生后,直接调用Observer的相关方法。
伪代码以下
Subject内使用List维护观察者
当事件发生,也就是方法f()中,循环通知观察者
省略了观察者的维护工做,也就是添加和删除
class Subject{
List<Observer> observerList = new ArrayList<>();
void f(){
//do sth...
for(Observer o:observerList){
//调用相关方法
o.doSthElse();
}
}

 

依赖倒置原则中,要求应该面向抽象进行编程,而不是面向细节
上面的结构中,不论是目标仍是观察者的扩展都不方便,因此抽象提取。
image_5c1af1b9_6dec
 
这就是观察者模式的基本结构。
抽象观察者角色Observer
为全部的具体的观察者定义一个接口,获得主题的通知信息后,进行同步响应。
通常包含一个方法叫作update()用以同步响应
抽象主题角色Subject
主题角色把全部观察者对象保存在集合中,提供管理工做,添加和删除
而且,提供通知工做,也就是调用相关观察者的update
具体主题角色ConcreteSubject
实现抽象主题接口协议,当状态方式发生变化时,对观察者进行通知
具体观察者角色ConcreteObserver
实现抽象观察者定义的接口,完成自身相关的同步更新活动

代码示例

抽象观察者角色,提供统一的更新方法
package observer;
public interface Observer {
void update();
}
抽象的Subject,内部使用List<Observer>保存观察者对象
提供了attach和dettach方法用于添加和删除观察者
而且提供了通知方法,就是遍历调用Observer
package observer;
import java.util.LinkedList;
import java.util.List;
 
public abstract class Subject {
 
    List<Observer> observerList;
     
    Subject() {
        observerList = new LinkedList<>();
    }
     
    void attach(Observer o) {
        observerList.add(o);
    }
     
    void dettach(Observer o) {
        observerList.remove(o);
    }
     
    void notifyObservers() {
        for (Observer o : observerList) {
            o.update();
        }
    }
}
具体的主题角色,他有一个行动方法,行动后通知其余的观察者
观察者在父类中列表里面定义
package observer;
public class ConcreteSubject extends Subject {
    public void action() {
        System.out.println("下雨了");
        super.notifyObservers();
    }
}
具体的观察者,实现具体的行动
package observer;
public class ConcreateObserver implements Observer {
    @Override
    public void update() {
        System.out.println("那我收衣服了");
    }
}
测试代码
package observer;
public class Test {
public static void main(String[] args) {
        Observer o1 = new ConcreateObserver();
        ConcreteSubject subject = new ConcreteSubject();
        subject.attach(o1);
        subject.action();
    }
}

 

image_5c1af1b9_179e
若是新增长一个观察者
package observer;
public class ConcreteObserver1 implements Observer {
    @Override
    public void update() {
        System.out.println("那我得把窗户关上");
    }
}

 

测试代码
image_5c1af1ba_3e32
如上图所示,有两个观察者(好比张三和李四),当包租婆大喊一声下雨了以后,他们都获得通知
张三去收衣服了,李四把本身的窗户关上了
 
以上就是观察者模式的简单示例。
 
观察者模式的核心在于对于观察者的管理和维护,以及发送通知。
消息的发布订阅,在程序中就是消息发布者调用订阅者的相关方法
观察者模式将发布者与订阅者进行解耦,再也不是直接的方法调用,经过引入Observer角色,完成了发布者与具体订阅者之间的解耦
也是一种形式的“方法调用”的解耦

Java的观察者模式

image_5c1af1ba_413f
 
观察者接口Observer
是一个interface,定义了一个update方法
void update(Observable o, Object arg);
接受两个参数,一个是被观察对象,一个是参数
被观察角色Observerable 等同于前文Subject
内部使用Vector维护观察者Observer
提供了对观察者的管理相关方法,添加、删除、计算个数、删除、删除全部等
 
Observerable内部使用标志位记录是否已经发生变化
    private boolean changed = false;
 
发生变更后,设置为true,通知后设置为false
image_5c1af1ba_7fcf
 
addObserver(Observer o) 添加指定观察者
deleteObserver(Observer o)  删除指定观察者
deleteObservers()      删除全部观察者
countObservers 返回观察者个数
notifyObservers()
notifyObservers(Object arg) 
通知全部观察者
一个无参,一个有参
参数传递给Observer的update
 
Observerable的实现原理很简单:
  • 使用Vector保存观察者,提供了添加、删除、删除所有的方法,而且能够返回观察者的个数。
  • 若是的确发生变更,将会通知全部的观察者
提供了两个版本的notifyObservers,一个有参,有个无参,参数对应update方法的第二个参数
image_5c1af1ba_6594
 
通知后,将会清除变更状态
 
image_5c1af1ba_7878
Observable中Vector<Observer>是私有的,也并无提供访问器,只是能够添加、删除、或者清除全部
因此子类并不知道具体的Observer
image_5c1af1ba_38a4
代码示例
package observer.java;
import java.util.Observable;
public class Subject extends Observable {
    public void changeState() {
        System.out.println("下雨了........");
        setChanged();
        notifyObservers();
    }
}

 

package observer.java;
import java.util.Observable;
import java.util.Observer;
public class Watcher1 implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("被观察者:" + o + " ,参数 " + arg + " ,观察者1");
    }
}

 

 

package observer.java;
import java.util.Observable;
import java.util.Observer;
public class Watcher2 implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("被观察者:" + o + " ,参数 " + arg + " ,观察者2");
    }
}
image_5c1af1ba_7d48
 
Subject 继承Observable类,自定义了changeState()方法
在方法中,调用
    setChanged();
    notifyObservers();
完成状态改变和通知。
 
从打印信息能够看得出来
update方法接收到的第一个参数,就是咱们的被观察者对象
第二个参数能够用来封装传递信息
 
因此在java中,除非场景特殊,你又不须要本身写观察者模式了,已经内置了
经过继承和实现相应的类和接口便可。
 
GOF的设计模式出版于95,JDK 1.0始于1996,因此,Java自然支持某些设计模式也很正常
并且,设计模式是经验总结,GOF将他们概括总结使之广为人知,可是并不表明这些经验前所未有
JDK的开发者人家自己就有这些“经验”也不足为奇。

与中介者模式区别

观察者模式用于一对多依赖场景中的解耦,经过引入Observer角色,将消息发布者与具体的订阅者进行解耦
中介者模式是将系统内部多对多的复杂耦合关系,借助于中介者进行解耦,将网状结构,简化为星型结构,将多对多转换为一对多 
 
他们都可以实现消息发布者与接收者的解耦,消息发布者都不知道具体的消息接收者
发起消息的Colleague同事类角色是被观察者,中介类Mediator是观察者,调用其余的同事类进行协做
 
尽管观察者模式强调“一致性通讯”
中介者模式强调“内部组件协做”
可是根本仍是方法执行时,须要同步调用其余对象
 
两个模式之间是一种相似的关系,在有些场景可替代转换。
若是协做关系比较简单,能够实现为一对多的形式,使用观察者模式
若是协做关系更加复杂,那么就可使用中介者模式

总结

观察者模式是在一对多的依赖场景中,对消息发布者和消息订阅者的解耦
在观察者和被观察者之间创建了一个抽象的耦合,而不是强关联
经过引入观察者角色,发布者不依赖具体的观察者,从而Subject和Observer能够独立发展
被观察者仅仅知道有N个观察者,他们以集合的形式被管理,都是Observer角色,可是彻底不知道具体的观察者
 
观察者模式的支持广播,被观察者会向全部的观察者发送消息。
 
增长新的观察者时,不须要修改客户端代码,只须要扩展Observer接口便可,知足开闭原则
 
一个观察者,也多是一个被观察者,若是出现观察者调用链,将会发生多米诺骨牌效应不断地进行调用,后果多是难以预知的。
因此要谨慎识别双重身份的对象,也就是便是观察者也是被观察者的对象。
 
被观察者不知道具体的观察者,也更不可能知道观察者具体的行为
当发生状态变化时,被观察者一个小举动,可能引发很大的性能消耗,而被观察者对此绝不知情,可能仍旧觉得云淡风轻。
 
当一个对象状态的改变,须要同时改变其余对象时,能够考虑观察者模式
当一个对象必须通知其余人时,可是他又不知道究竟是谁时,能够考虑观察者模式
或者将一个抽象模型中的两个关联部分解耦,以便独立发展,提升复用性,解耦不表示断开,仍旧须要联系,能够借助于观察者模式进行联系
总之
观察模式经过引入观察者角色,将调用者与被调用者解耦,经过观察者角色联系。
但凡相似“广播”“发布订阅”的场景,均可以考虑是否可用。
相关文章
相关标签/搜索