设计模式(二)观察者模式

引子

 观察者模式,就是观察别人在干嘛,作为观察者,被观察者的一举一动都要了如指掌,作为被观察者,该干嘛干嘛,并不知道自己的一切行为都以为暴露在别人的监控之下。是不是有点谍战的意思。我们就用古代三国演义里诸葛亮和间谍来类比,演示一下什么是观察者模式。

来看看官方定义:Observer Pattren(观察者模式)也叫做发布订阅模式,定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会地到通知并被自动更新。

先来看看总体类图

设计模式(二)观察者模式

实例类图如下

设计模式(二)观察者模式

被观察者接口

public interface IZhuGeLiang {
    void planB();
    void attack();
}
public interface Observable {
    //增加一个观察者
    void addObserver(Observer observer);

    //删除一个观察者
    void deleteObserver(Observer observer);

    //既然要观察,我发生了改变他也应该有所动作,通知观察者
    void notifyObservers(String context);
}

被观察者实现类

public class ZhuGeLiang implements Observable,IZhuGeLiang{
    //定义一个变长数组,存放所有观察者
    private ArrayList<Observer> observers = new ArrayList<>();

    @Override
    public void addObserver(Observer observer) {
        this.observers.add(observer);
    }

    @Override
    public void deleteObserver(Observer observer) {
        this.observers.remove(observer);
    }

    @Override
    public void notifyObservers(String context) {
        for (Observer ob : observers) {
            ob.update(context);
        }
    }

    //诸葛亮要谋划计策了
    public void planB() {
        System.out.println("诸葛亮:开始谋划计策了...");
        //通知所有观察者
        this.notifyObservers("诸葛亮在密谋");
    }

    //诸葛亮要准备起兵了
    public void attack() {
        System.out.println("诸葛亮:准备战斗了...");
        this.notifyObservers("诸葛亮要起兵");
    }
}

观察者接口

public interface Observer {
    //一旦发现别人有动静,就立马行动起来
    void update(String context);
}

具体的观察者

public class WangSiTu implements Observer {
    //王司徒视诸葛亮为不共戴天之敌
    @Override
    public void update(String context) {
        System.out.println("王司徒:观察到诸葛亮有动作,要准备报告曹丞相...");
        this.reportToCaoCao(context);
        System.out.println("王司徒:汇报完毕...\n");
    }

    //汇报给曹操
    public void reportToCaoCao(String reportContext) {
        System.out.println(reportContext + "!" + "王司徒:报告,丞相!诸葛村夫有动作了!!!");
    }
}
public class LiuBei implements Observer {
    //刘备
    @Override
    public void update(String context) {
        System.out.println("刘备:观察到诸葛亮有动作,干得漂亮...");
        this.happy(context);
        System.out.println("刘备:老诸很牛逼啊...\n");
    }

    //汇报给曹操
    public void happy(String reportContext) {
        System.out.println(reportContext + "!" + "刘备:回来重赏诸葛村夫!");
    }
}
public class SunQuan implements Observer {
    //孙权老哥
    @Override
    public void update(String context) {
        System.out.println("孙权:观察到诸葛亮有动作,干我毛事...");
        this.kanReNao(context);
        System.out.println("孙权:看看热闹...\n");
    }

    //汇报给曹操
    public void kanReNao(String reportContext) {
        System.out.println(reportContext + "!" + "孙权:我就安安静静的当个吃瓜群众...");
    }
}

场景类

public class Clinet {
    public static void main(String[] args) {
        //生产出三个观察者
        Observer wst = new WangSiTu();
        Observer lb = new LiuBei();
        Observer sq = new SunQuan();

        //定义出诸葛亮
        ZhuGeLiang zgl = new ZhuGeLiang();

        zgl.addObserver(wst);
        zgl.addObserver(lb);
        zgl.addObserver(sq);

        zgl.planB();
        //zgl.attack();
    }
}

运行结果

设计模式(二)观察者模式

观察者模式的优点

  • 观察者和被观察者之间是抽象耦合。
  • 建立一套触发机制。

观察者模式的缺点

    观察者模式需要考虑一下开发效率和运行效率问题,一个观察者,多个被观察者,开发和调试就会比较复杂,而且在JAVA中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。在这种情况下,一般考虑采用异步的方式。多级触发时的效率更是让人担忧,在设计时注意考虑。

观察者模式使用场景

  • 关联行为场景。
  • 事件多级触发场景。
  • 跨系统的消息交换场景,如消息队列的处理机制。

JAVA中的观察者模式

    JDK中提供了:java.util.Observable实现类和java.util.Observer接口,上述例子我们可以改造一下。

优化后的被观察者

public class ZhuGeLiang implements Observable,IZhuGeLiang{
    //此时的Observable是java.util.Observable下的

    //诸葛亮要谋划计策了
    public void planB() {
        System.out.println("诸葛亮:开始谋划计策了...");
        //通知所有观察者
        super.setChanged();
        super.notifyObservers("诸葛亮在密谋");
    }

    //诸葛亮要准备起兵了
    public void attack() {
        System.out.println("诸葛亮:准备战斗了...");
        super.setChanged();
        super.notifyObservers("诸葛亮要起兵");
    }
}

优化后的观察者

public class WangSiTu implements Observer {
    //这里的Observer是java.util.Observer下的

    //王司徒视诸葛亮为不共戴天之敌
    @Override
    public void update(Observable observable, Object obj) {
        System.out.println("王司徒:观察到诸葛亮有动作,要准备报告曹丞相...");
        this.reportToCaoCao(obj.toString());
        System.out.println("王司徒:汇报完毕...\n");
    }

    //汇报给曹操
    public void reportToCaoCao(String reportContext) {
        System.out.println(reportContext + "!" + "王司徒:报告,丞相!诸葛村夫有动作了!!!");
    }
}