31. 观察者模式

定义

观察者模式(Observer Pattern):定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

通俗理解

“敌不动,我不动,敌若动,我先动”,这是《兵法》一个经典的战略,意思是说,在战场上,我们要注意好敌军的动向,根据敌军的动向来指定自己这一方的作战方针,如果敌人不行动,我们就按兵不动,如果敌人行动,我们就需要比敌人更先行动。这一句话后来被教员融合当时在社会斗争情况总结为"敌来我走,敌驻我扰,敌退我追"!这也是根据敌人的行动来执行自己的战略。

类似于这种,根据敌人的行动类改变自己的方针,就是观察者模式最好的诠释。当敌人行动的时候,他的状态改变了,那么也会影响到我军的状态。当然,在观察者模式当中,定义的是一对多的依赖关系,这在生活中的含义就是,当一军行动的时候,会引起联合军队的行动。

这样的例子,不仅仅存在战场上,在我们生活中也是如此。红绿灯变绿的时候,所有的汽车都会开动;当号令枪响的时候,所有的运动员都会健步如飞;当下暴雨的时候,所有的人都会躲雨。

示例

业务为红绿灯变绿的时候,所有的汽车都会开动。

渣渣程序

红绿灯

public class TrafficLight {
    private ELightType eLightType;
    private Car car;
    public TrafficLight(Car car) {
        this.car = car;
    }
    public void becomeGreen() {
        eLightType = ELightType.GREEN;
        System.out.println("交通灯变绿了"+eLightType);
    }
    public void becomeYellow() {
        eLightType = ELightType.YELLOW;
        System.out.println("交通灯变黄了"+eLightType);
    }
    public void becomeRed() {
        eLightType = ELightType.RED;
        System.out.println("交通灯变红了"+eLightType);
    }
    public void notifyCar() {
        switch (eLightType) {
            case RED:car.stop();break;
            case GREEN:car.move();break;
            case YELLOW:car.stop();break;
            default:car.stop();
        }
    }
    public enum ELightType {
        GREEN,YELLOW,RED
    }
}

车辆

public class Car {
    private boolean isMove = false;
    //setter、getter方法省略
    public void move() {
        isMove = true;
        System.out.println("汽车开动了"+isMove);
    }
    public void stop() {
        isMove = false;
        System.out.println("汽车停止了"+isMove);
    }
}

程序入口

public class Main {
    public static void main(String[] args) {
        TrafficLight trafficLight = new TrafficLight(new Car());
        trafficLight.becomeGreen();
        trafficLight.notifyCar();
    }
}
//交通灯变绿了GREEN
//汽车开动了true

上面基本上是实现了观察者模式的理念,但是演示了什么叫不面向接口编程,什么叫紧耦合。存在如下问题:

  1. 红绿灯和车辆是紧耦合的,如果我需要添加行人监听,那么我就需要修改交通灯的类,不利于扩展;
  2. 车辆(订阅者)的类的更新方法复杂,没有统一接口,如果添加行人的更新状态根本是不可能的。
  3. 这里提示车辆更新的方法只传递了一个类型,如果这个类型十分复杂的情况下,那么参数很难搞定。

优化

类图

程序

观察者接口与车辆实现

public interface IObserver {
    void update(ISubject subject, Object args);
}
public class Car implements IObserver {
    private boolean isMove = false;
    // 省略setter、getter方法
    public void move() {
        isMove = true;
        System.out.println("汽车开动了"+isMove);
    }
    public void stop() {
        isMove = false;
        System.out.println("汽车停止了"+isMove);
    }
    @Override
    public void update(ISubject subject, Object args) {
        TrafficLight.ELightType type = (TrafficLight.ELightType) args;
        update(type);
    }
    public void update(TrafficLight.ELightType type) {
        switch (type) {
            case RED:stop();break;
            case GREEN:move();break;
            case YELLOW:stop();break;
            default:stop();
        }
    }
}

被观察对象的接口与交通灯实现

public interface ISubject {
    void addObserver(IObserver observer);
    void removeObserver(IObserver observer);
    void removeObservers();
    void notifyObservers(Object args);
    void notifyObservers();//模式里面没有这个方法
}
public class TrafficLight implements ISubject{
    private LinkedTransferQueue<IObserver> observers;
    public TrafficLight() {
        this.observers = new LinkedTransferQueue<>();
    }
    private ELightType eLightType;
    public ELightType geteLightType() {
        return eLightType;
    }
    public void seteLightType(ELightType eLightType) {
        this.eLightType = eLightType;
    }
    public void becomeGreen() {
        eLightType = ELightType.GREEN;
        System.out.println("交通灯变绿了"+eLightType);
    }
    public void becomeYellow() {
        eLightType = ELightType.YELLOW;
        System.out.println("交通灯变黄了"+eLightType);
    }
    public void becomeRed() {
        eLightType = ELightType.RED;
        System.out.println("交通灯变红了"+eLightType);
    }
    @Override
    public void addObserver(IObserver observer) {
        this.observers.add(observer);
    }
    @Override
    public void removeObserver(IObserver observer) {
        this.observers.remove(observer);
    }
    @Override
    public void removeObservers() {
        this.observers.clear();
    }
    @Override
    public void notifyObservers(Object args) {
        for(IObserver observer : observers) {
            observer.update(this, args);
        }
    }
    @Override
    public void notifyObservers() {
        notifyObservers(eLightType);
    }
    public enum ELightType {
        GREEN,YELLOW,RED
    }
}

程序入口

public class Main {
    public static void main(String[] args) {
        ISubject trafficLight = new TrafficLight();
        IObserver car = new Car();

        trafficLight.addObserver(car);
        ((TrafficLight) trafficLight).becomeGreen();
        trafficLight.notifyObservers();
    }
}
//交通灯变绿了GREEN
//汽车开动了true

通过上面的过程,如果我们需要添加一个新的订阅者,那么只需要添加一个新的订阅者并实现IObserver,然后写上相关的update方法就可以,而且这种方式可以增加多个订阅者,不像一开始一样,就只能绑定一个订阅者。

优点

  1. 对象之间的同步通信;
  2. 可以通知一个或多个关联对象;
  3. 对象之间的关系以松耦合关联,不互相一样,增加订阅者类的时候只需要扩展添加新类即可。

缺点

  1. 松耦合导致代码不清晰,难理解;
  2. 通知多个订阅者浪费时间,而且可能会出现系统崩溃等情况;
  3. 如果是异步通知的话,不能保证通知到位;
  4. 通知者并不知道是否通知到位。

应用场景

  1. 监听事件驱动程序设计中的外部事件;
  2. 监听/监视某个对象的变化;
  3. 发布/订阅者模型。

实例

  1. JDK的java.util.Observable和java.util.Observer接口;
  2. MVC模型。

程序

e31_observer_pattern

https://www.jianshu.com/p/a615c709ec25

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容