掘金 后端 ( ) • 2024-04-17 09:51

观察者模式

  • 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。
  • 这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

进一步阐述:观察者是一种行为型设计模式,当主题对象发生变化的行为时,会通知所有的观察者,使其所有的观察者去自动更新自己。

观察者模式包含四种角色:

  • Subject(抽象主题角色):抽象主题角色会提供三个方法,分别可以增加观察者对象、删除观察者对象和自己状态发生变化时通知所有的观察者对象更新。
  • ConcreteSubject(具体主题角色):它实现了抽象主题角色接口,它聚合了所有的观察者对象,把所有观察者对象保存在一个集合里,可以有任意数量的观察者(这里体现了一对多的关系),提供具体的将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知的功能。
  • Observer(抽象观察者接口):提供一个具体主题状态变化时所调用的抽象更新方法,使得在得到主题更改通知时更新自己。
  • ConcrereObserver(具体观察者),实现了抽象观察者接口,并重写了更新方法去实现具体的逻辑处理,也可以在这个更新方法里面回调主题对象,以获取到主题对象的数据。

案例:明星发布动态后关注他的粉丝能够及时知道并展示关注的明星所发出的最新的动态为例。明星Strat接口就是抽象主题角色,具体明星ConcreteStar类就是具体主题角色,粉丝Fan接口就是抽象观察者接口,台湾省粉丝TailWanFan类和浙江省粉丝ZheJiangFan类就是具体观察者。

UML类图:

观察者模式.jpg

客户端Client类:

/**
 * 使用观察者模式的客户端
 */
public class Client {
​
    public static void main(String[] args) {
        //创建主题
        ConcreteStar star = new ConcreteStar();
​
        //创建观察者
        TailWanFan tailWanFan = new TailWanFan();
        ZheJiangFan zheJiangFan = new ZheJiangFan();
​
        //设置观察者需要观察的主题
        star.addFan(tailWanFan);
        //新增一个观察者
        star.addFan(zheJiangFan);
        //删除一个观察者
        star.removeFan(zheJiangFan);
​
        //明星发布一条动态
        star.publishing("王力宏离婚了,其具体原因是……此处省略5000字");
    }
}

明星Star接口:

/**
 * 明星接口(抽象主题角色)
 */
public interface Star {
​
    /**
     * 新增粉丝方法
     */
    void addFan(Fan fan);
​
    /**
     * 删除粉丝方法
     */
    void removeFan(Fan fan);
​
    /**
     * 通知粉丝有新动态的方法
     */
    void notifyFan();
}

具体明星ConcreteStar类:

/**
 * 具体明星类(具体主题角色)
 * 包含最新的动态的内容,还包含关注的所有粉丝,当明星发动态的时候,这里使用推模式主动调用
 * 粉丝对象的更新方法,然后让关注的粉丝知道最新的态的内容。
 */
public class ConcreteStar implements Star {
​
    /*
     * 最新发布的动态内容
     */
    private String starDynamicContent;
​
    /*
     * 关注我的粉丝们
     */
    private List<Fan> myFanList = new ArrayList<>();
​
​
    /**
     * 发布一条新的动态方法
     *
     * @param toPublishContent
     */
    public void publishing(String toPublishContent) {
        this.starDynamicContent = toPublishContent;
        //由于使用的是推模式,所以主动调用
        notifyFan();
    }
​
    @Override
    public void addFan(Fan fan) {
        myFanList.add(fan);
    }
​
    @Override
    public void removeFan(Fan fan) {
        myFanList.remove(fan);
    }
​
    @Override
    public void notifyFan() {
        for (Fan f : myFanList) {
            f.update(starDynamicContent);
        }
    }
}

粉丝Fan接口:

/**
 * 粉丝接口
 */
public interface Fan {
​
    /**
     * 更新动态内容方法
     */
    void update(String newDnmcCtt);
}

台湾省粉丝TailWanFan类:

/**
 * 台湾省粉丝类
 */
public class TailWanFan implements Fan {
​
    /*
     * 关注的明星发的动态内容
     */
    private String dynamicContent;
​
    /**
     * 更新动态内容方法
     */
    @Override
    public void update(String newDnmcCtt) {
        this.dynamicContent = newDnmcCtt;
        display();
    }
​
    /**
     * 显示关注明星动态方法
     */
    public void display() {
        System.out.println("台湾粉丝知道了关注的明星的最新动态是:" + dynamicContent);
    }
}

浙江省粉丝ZheJiangFan类:

/**
 * 浙江省粉丝类
 */
public class ZheJiangFan implements Fan {
​
    /*
     * 关注的明星发的动态内容
     */
    private String dynamicContent;
​
    /**
     * 更新动态内容方法
     */
    @Override
    public void update(String newDnmcCtt) {
        this.dynamicContent = newDnmcCtt;
        display();
    }
​
    /**
     * 显示关注明星动态方法
     */
    public void display() {
        System.out.println("浙江粉丝知道了关注的明星的最新动态是:" + dynamicContent);
    }
}

总结:

  • 观察者模式实现了表示层和数据逻辑层的分离,这样就降低了表示层和数据逻辑层的耦合度,遵守了单一职能原则。
  • 在观察目标和观察者之间建立了一个抽象的耦合,使得观察者和观察目标之间可以独立的变化和扩展,观察目标依赖的是一组观察者的抽象,这样就遵守了依赖倒置原则。
  • 观察者模式支持广播通信,这样就在观察目标状态发生变化的时候可以很好的去通知到所有的观察者们,当要增加新的观察者或者删除观察者的时候只需要调用原有观察目标对象的方法即可,这样就遵守了开闭原则,大大提高了系统的可维护性和可扩展性。
  • 如果一个观察目标对象有很多直接和间接的观察者的情况,此时要通知所有的观察者就会花费很多时间,对系统效率很有很大影响。
  • 在观察者和观察目标之间如果有循环依赖,那么观察目标会触发它们之间进行循环调用,可能导致系统崩溃。

典型运用场景举例:

  • 电商系统中的下单场景: 当用户购买某件商品下了一个订单时, 此时需要通知库存系统减少库存、通知商家系统发货、通知支付系统收钱、甚至还会通知关系中心使当前用户关注该商家。
  • 聊天室系统中的在群聊中发消息场景: 当一个用户在群聊中发送了一条信息时, 此时需要通知到所有在这个群聊中的群成员。