观察者模式

2019/08/29 设计模式 共 2942 字,约 9 分钟

观察者模式

观察者模式非常容易理解,好比说微博,你关注了一个明星的微博,每当这个明星发微博的时候总会给你发送通知。那么在这个例子中,你就是观察者,其所有关注这个明星的人也是观察者,而这个明星就是被观察者,每当被观察者某个状态发生变化(发一条微博),就会给所有的观察者发送通知。

定义

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

类图

观察者模式类图

角色结构

  • Subject被观察者:定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者(管理观察者),并通知观察者。
  • Observer观察者:观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。
  • WeiBo具体的被观察者:定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。
  • UserA、UserB具体的观察者:每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。

实例

微博作为当今获取新闻、追星等等行为的最佳途径,大为火热。现在有A、B两个微博用户,先后关注了某微博大VSomeBody,希望在大V发微博的第一时间收到通知,成为沙发、板凳。

  1. 首先创建观察者接口
public interface Observer {

    /**
     * 获取观察者的微博用户名
     * @return String
     */
    String getUserName();

    /**
     * 通知更新方法
     * 当被观察者发生变化时,观察者通过这个方法获得通知
     * @param msg 发微博的内容
     */
    void update(String msg);
}
  1. 创建两个具体观察者,并实现观察者接口。
// 第一种观察者收到巨星发新微博的通知后,行为是点赞
public class UserA implements Observer {

    private String name;

    public UserA(String name) {
        this.name = name;
    }

    @Override
    public String getUserName() {
        return this.name;
    }

    @Override
    public void update(String msg) {
        System.out.println(this.name + "收到某人发新微博的通知,并点了个赞");
    }
}

// 第二种观察者的行为是收藏这条微博
public class UserB implements Observer {

    private String name;

    public UserB(String name) {
        this.name = name;
    }

    @Override
    public String getUserName() {
        return this.name;
    }

    @Override
    public void update(String msg) {
        System.out.println(this.name + "收到某人发新微博的通知,并收藏了这条微博");
    }
}
  1. 创建被观察者抽象类
public abstract class Subject {

    private List<Observer> observers = new ArrayList<>();

    /**
     * 增加观察者
     * @param o
     */
    public void add(Observer o) {
        observers.add(o);
        System.out.println("用户 " + o.getUserName() + " 关注");
    }

    /**
     * 减少观察者
     * @param o
     */
    public void delete(Observer o) {
        observers.remove(o);
        System.out.println("用户 " + o.getUserName() + " 取关");
    }

    /**
     * 通知所有观察者
     * @param msg
     */
    public void notifyObservers(String msg) {
        for (Observer o:observers) {
            o.update(msg);
        }
    }
}
  1. 创建微博巨星,并继承被观察者抽象类,他还有一个发微博的方法,会调用通知所有观察的方法
public class SomeBody extends Subject {

    public void send(String msg) {
        System.out.println("某人发微博了,内容是:" + msg);
        notifyObservers(msg);
    }
}
  1. 便携测试类代码及运行结果
public class Test {

    public static void main(String[] args) {
        // 创建巨星对象
        SuperStar superStar = new SuperStar();
        superStar.send("这是我的第一条微博,但是现在关注为0,没有人收到");
        // 创建第一个用户,关注巨星
        UserA lonelyWolf = new UserA("孤独的狼");
        superStar.add(lonelyWolf);
        superStar.send("2. 你好" + lonelyWolf.getUserName());
        // 创建第二个用户,关注巨星
        UserB sheep = new UserB("寂寞的羊");
        superStar.add(sheep);
        superStar.send("2、欢迎新同学");
        // 第二个用户取关
        superStar.delete(sheep);
        superStar.send("3、床前明月光,你走吧");
    }
}
巨星发微博了,内容是:这是我的第一条微博,但是现在关注为0,没有人收到
用户 孤独的狼 关注
巨星发微博了,内容是:2. 你好孤独的狼
孤独的狼收到某人发新微博的通知,并点了个赞
用户 寂寞的羊 关注
巨星发微博了,内容是:2、欢迎新同学
孤独的狼收到某人发新微博的通知,并点了个赞
寂寞的羊收到某人发新微博的通知,并收藏了这条微博
用户 寂寞的羊 取关
巨星发微博了,内容是:3、床前明月光,你走吧
孤独的狼收到某人发新微博的通知,并点了个赞

观察者模式的优缺点

优点

  1. 观察者和被观察者之间是抽象耦合
    • 观察者和被观察者都非常容易扩展
  2. 可以方便的建立一套触发机制
    • 如果观察者之间存在某种出发关系,如用户A点赞之后,用户B才会收藏,观察者模式可以完美实现这个机制

缺点

  1. 如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间。
  2. 如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

使用场景

  1. 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
  2. 事件多级触发场景。
  3. 跨系统的消息交换场景,如消息队列的处理机制。

Java内置的观察者模式

Java内置实现了观察者模式,观察者接口java.util.Observer,被观察者类java.util.Observable

  • 值得一提的是,在Observable类中有一个布尔类型的changed属性,是用来标记允许通知的,只有该属性为true时,才会通知所有的观察者。
  • 因为Java不支持多继承,如果继承Observable类,那就无法继承其他类了。

源代码

GitHub地址

版权声明:本文为Planeswalker23所创,转载请带上原文链接,感谢。

文档信息

Search

    Table of Contents