观察者模式详解

观察者模式(Observer Pattern)

定义

观察者模式定义了对象之间的一对多的依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

通俗的定义:发布订阅模式(报社-读者)

模式总结过程

公司经营电商系统,有自己的工厂,当工厂有新产品产出时,通知仓库更新库存、通知商店拿货、通知客户购买

  1. 初步的实现如下
// 自家工厂
public class Factory {
    public void produce(String goods) {
        System.out.println("工厂生产了新产品: " + goods);
        // 通知库存更新
        new Repertory().updateData(goods);
        // 通知商店拿货
        new Store().sale(goods);
        // 通知客户购买
        new Customer().buy(goods);
    }
}
// 仓库
public class Repertory {

    public void updateData(String goods) {
        System.out.println("仓库更新库存: " + goods);
    }
}
// 商店
public class Store {

    public void sale(String goods) {
        System.out.println("商店拿货销售: " + goods);
    }
}
// 客户
public class Customer {

    public void buy(String goods) {
        System.out.println("客户购买: " + goods);
    }
}
// 客户端
public class Client {

    public static void main(String[] args) {
        Factory factory = new Factory();
        factory.produce("智能机器人");
    }
}
/*
工厂生产了新产品: 智能机器人
仓库更新库存: 智能机器人
商店拿货销售: 智能机器人
客户购买: 智能机器人
*/

这样的设计,有几个问题

  1. 针对实现编程,而非针对接口编程
  2. 如果工厂有新产品后的通知目标增加或减少,需要对原代码进行修改
  3. 无法在运行时动态地增加或删除通知目标,如我们不想通知客户了
  4. 耦合程度很高
  1. 针对上述问题,我们进行优化,工厂类抽象一个接口出来,所有目标也抽象一个接口出来,然后工厂类持有观察者接口的引用
// 主题接口(消息发布者)
public interface Subject {

    // 注册观察者,也就是加入需要通知的目标
    void registerObserver(Observer observer);

    // 移除观察者,也就是删除不需要通知的目标
    void removeObserver(Observer observer);

    // 通知所有观察者,也就是通知所有的目标
    void notifyObservers();
    
    // 生产
    void produce(String goods);
}
// 自家工厂
public class Factory implements Subject {

    // 观察者集合
    private List<Observer> list;

    // 产品
    private String goods;

    public Factory() {
        list = new ArrayList<>();
    }

    // 生产产品
    @Override
    public void produce(String goods) {
        System.out.println("工厂生产了新产品: " + goods);
        this.goods = goods;
        notifyObservers();
    }

    @Override
    public void registerObserver(Observer observer) {
        list.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        list.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : list) {
            observer.update(goods);
        }
    }
}
// 抽象观察者(这里自己定义,而非使用jdk源码中的Observer)
public interface Observer {
    // 统一一个方法让工厂类调用
    void update(String goods);
}
// 仓库
public class Repertory implements Observer {

    public void updateData(String goods) {
        System.out.println("仓库更新库存: " + goods);
    }

    @Override
    public void update(String goods) {
        updateData(goods);
    }
}
// 商店
public class Store implements Observer {

    public void sale(String goods) {
        System.out.println("商店拿货销售: " + goods);
    }

    @Override
    public void update(String goods) {
        sale(goods);
    }
}
// 客户
public class Customer implements Observer {

    public void buy(String goods) {
        System.out.println("客户购买: " + goods);
    }

    @Override
    public void update(String goods) {
        buy(goods);
    }
}
// 客户端
public class Client {

    public static void main(String[] args) {
        Subject subject = new Factory();//工厂

        Repertory repertory = new Repertory();//仓库
        Store store = new Store();//商店
        Customer customer = new Customer();//客户

        subject.registerObserver(repertory);//添加仓库为通知目标
        subject.registerObserver(store);//添加商店为通知目标
        subject.registerObserver(customer);//添加客户为通知目标

        subject.produce("智能机器人");

        // 客户不想收到通知
        subject.removeObserver(customer);

        System.out.println("--------");

        subject.produce("智能家具");
    }
}
/*
工厂生产了新产品: 智能机器人
仓库更新库存: 智能机器人
商店拿货销售: 智能机器人
客户购买: 智能机器人
--------
工厂生产了新产品: 智能家具
仓库更新库存: 智能家具
商店拿货销售: 智能家具
*/

再发一次初步实现的问题

  1. 针对实现编程,而非针对接口编程
  2. 如果工厂有新产品后的通知目标增加或减少,需要对原代码进行修改
  3. 无法在运行时动态地增加或删除通知目标,如我们不想通知客户了
  4. 耦合程度很高

现在,我们的实现就很不错了,针对初步现实的问题,我们再来对比一下

  1. 我们针对接口编程,并非针对实现编程
  2. 通知目标增加时,只需要再实现Observer接口,再把这个实现registerObserver到工厂即可;减少时,工厂类调用removeObserver()即可
  3. 运行时,也可以动态地增加或删除通知目标(registerObserver/removeObserver)
  4. 耦合程度很低(工厂类只依赖观察者接口的集合,不需要知道具体观察者的实现)
优点
  1. 解耦,符合开闭原则
  2. 符合好莱坞原则:别来找我,我找你们。也就是被观察者通知所有观察者
缺点
  1. 运行效率较低,一个被观察者,多个观察者时,开发代码和调试会比较复杂
  2. 消息的通知是默认顺序执行的,若其中一个观察者卡壳,会影响到此观察者后面的观察者执行,影响整体的执行, 多级触发时的效率更让人担忧
应用
  1. 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变
  2. 分布订阅型场景
jdk中的观察者模式
  1. 抽象观察者接口
package java.util;

/**
 * A class can implement the <code>Observer</code> interface when it
 * wants to be informed of changes in observable objects.
 *
 * @author  Chris Warth
 * @see     java.util.Observable
 * @since   JDK1.0
 */
public interface Observer {
    /**
     * This method is called whenever the observed object is changed. An
     * application calls an <tt>Observable</tt> object's
     * <code>notifyObservers</code> method to have all the object's
     * observers notified of the change.
     *
     * @param   o     the observable object.
     * @param   arg   an argument passed to the <code>notifyObservers</code>
     *                 method.
     */
    void update(Observable o, Object arg);
}
  1. 主题类(对比工厂类)
package java.util;

public class Observable {
    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);
    }
    // ...
    // 只列一小部分方法
}

注意:这里是一个类,虽然实现了大部分功能,但并不是接口

使用jdk API实现上述例子
// 自家工厂
// 继承jdk中的Observable类
public class Factory extends Observable {

    public void produce(String goods) {
        System.out.println("工厂生产了新产品: " + goods);
        setChanged();//只有状态改变才会通知观察者
        notifyObservers(goods);
    }
}
// 仓库
// 实现jdk中的Obserer接口
public class Repertory implements Observer {

    private void updateData(String goods) {
        System.out.println("仓库更新库存: " + goods);
    }

    @Override
    public void update(Observable o, Object arg) {
        updateData((String) arg);
    }
}
// 商店
// 实现jdk中的Obserer接口
public class Store implements Observer {

    private void sale(String goods) {
        System.out.println("商店拿货销售: " + goods);
    }

    @Override
    public void update(Observable o, Object arg) {
        sale((String) arg);
    }
}
// 客户
// 实现jdk中的Obserer接口
public class Customer implements Observer {

    private void buy(String goods) {
        System.out.println("客户购买: " + goods);
    }

    @Override
    public void update(Observable o, Object arg) {
        buy((String) arg);
    }
}
// 客户端
public class Client {

    public static void main(String[] args) {
        Factory factory = new Factory();//工厂

        Repertory repertory = new Repertory();//仓库
        Store store = new Store();//商店
        Customer customer = new Customer();//客户

        factory.addObserver(repertory);//添加仓库为通知目标
        factory.addObserver(store);//添加商店为通知目标
        factory.addObserver(customer);//添加客户为通知目标

        factory.produce("智能机器人");

        // 客户不想收到通知
        factory.deleteObserver(customer);
        System.out.println("--------");

        factory.produce("智能家具");
    }
}
/*
工厂生产了新产品: 智能机器人
客户购买: 智能机器人
商店拿货销售: 智能机器人
仓库更新库存: 智能机器人
--------
工厂生产了新产品: 智能家具
商店拿货销售: 智能家具
仓库更新库存: 智能家具
*/

优点:省事,被观察者的许多功能已经实现,直接使用即可,也提供了观察者接口

缺点:

  1. 被观察者通知观察者时,是倒序通知,这个要注意
  2. 被观察者是一个类,提供方便的同时,也限制了工厂类的扩展
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值