java设计模式之观察者模式

1、前言

        观察者模式:当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)又叫发布-订阅模式(Publish/Subscribe)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

2、应用场景及说明

       主要用来解决一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

从游戏角度出发,就是从被动定时拉取,变成主动推送的方式。比如红色警戒中【尤里】控制对方工具,只有【尤里】时候对方工具才能解除控制。这里解除控制的时候,就只直接通知对方工具解除控制。否则就可能出现对方工具定时检测【尤里】的是否存活来判断是否解除控制。

       从业务来讲,更多的应用在消息的推动上,比如委托方下单后,需要通知物流公司和库存的库管有一辆车即将进入你们系统。只需要物流系统和库存系统订阅委托方下单的功能,委托方下单后就会直接通知物流和库存,又比如车辆信息修改,也可以直接通知物流库存。

3、角色说明

从上面我们可以看到,这里面包含了两大类(主题和观察者)一共四个角色:

(1)Subject:抽象主题,他把所有观察者对象保存在一个集合里,可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。

(2)ConcreteSubject:具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。

(3)Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。

(4)ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

其中我们主题(Subject)就是车辆信息变化,具体的主题(ConcreteSubject)就是委托方下单业务,具体的观察者(ConcrereObserver)就是物流业务和库存业务

4、代码案例

UML类图:

抽象的主题:

/**
 * 主题
 *
 * @author: ws
 * @date: 2020/9/21 13:32
 */
public interface Subject {

    /**
     * 添加观察者
     * @param observer
     */
    void addObserver(Observer observer);

    /**
     * 删除观察者
     * @param observer
     */
    void removeObserver(Observer observer);

    /**
     * 通知观察者
     */
    void notifyObservers();
}

抽象的观察者:

/**
 * 观察者
 *
 * @author: ws
 * @date: 2020/9/21 13:40
 */
public interface Observer {
    void update();
}

具体的主题-车辆信息变更:

/**
 * 车辆更新/变化主题
 *
 * @author: ws
 * @date: 2020/9/21 13:34
 */
public class VehicleUpdateSubject implements Subject{

    // 线程安全的集合
    private Vector<Observer> vectors = new Vector<>();

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

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

    @Override
    public void notifyObservers() {
        for (Observer observer : vectors) {
            observer.update();
        }
    }

    public void vehicleUpdate(String msg){
        System.out.println("*******"+ msg +"********");
        this.notifyObservers();
    }
}

观察者-物流系统:

/**
 * 观察者-物流
 *
 * @author: ws
 * @date: 2020/9/21 13:45
 */
public class WlObservable implements Observer{

    private Subject subject;
    private String name;

    public WlObservable(Subject subject, String name) {
        this.subject = subject;
        this.name = name;

        subject.addObserver(this);
    }

    @Override
    public void update() {
        System.out.println("观察者:【"+name+"】接受到了通知!");
    }
}

观察者-库存系统:

/**
 * 观察者-仓库
 *
 * @author: ws
 * @date: 2020/9/21 13:49
 */
public class StoreObservable implements Observer{

    private Subject subject;
    private String name;

    public StoreObservable(Subject subject, String name) {
        this.subject = subject;
        this.name = name;

        subject.addObserver(this);
    }

    @Override
    public void update() {
        System.out.println("观察者:"+name+"接受到了通知!");
    }
}

客户端测试:

/**
 * 客户端测试
 *
 * @author: ws
 * @date: 2020/9/21 13:51
 */
public class PatternClient {

    public static void main(String[] args) {
        // 设置具体的主题
        VehicleUpdateSubject subject = new VehicleUpdateSubject();

        // 添加观察者(订阅主题)
        Observer wlObserver = new WlObservable(subject, "物流系统");
        Observer storeObserver = new WlObservable(subject, "库存系统");

        // 车辆发生改变
        subject.vehicleUpdate("委托方下单-新增车辆");
    }
}

执行结果:

5、分析观察者模式

不管是公司业务还是游戏中的尤里,使用观察者模式除了通知类的模式之外,这其实也是一种推拉模型。

  1. 推模型就是主动推送主题消息,不管观察者是否真的需要,只要订阅就会直接推送;
  2. 拉模型就是观察者根据自己需要被动拉取主题中的信息,但是并不知道主题中的信息什么时候更新。

6、观察者模式 VS 发布订阅模式(Pub-Sub Pattern)

观察者模式也叫发布-订阅模式,但是又区别我们常说的发布订阅。现在所说的发布订阅已经独立于观察者模式,成为另外一种不同的设计模式,以消息中间件最为常见。

观察者模式里面,update方法所在的实例对象,就是被观察者(Subject,或者叫Observable),它只需维护一套观察者(Observer)的集合,这些Observer实现相同的接口,Subject有改变时,直接通知所有观察者,至于观察者是否需要,主题并不关心,只要注册了,就会收到改变的信息。

发布订阅模式中发布者的消息发送者不会将消息直接发送给订阅者,而是通过第三个组件,该组件称为消息代理或调度中心或中间件,它维持着发布者和订阅者之间的联系,过滤所有发布者传入的消息并相应地分发它们给订阅者。

7、观察者模式的延伸

  •     发布订阅常用的消息中间件(消息队列),redis中的发布订阅等。其中以消息中间件最为常用,用来异步处理分布式系统中的消息通知问题,以此来解耦。
  •     Spring的事件机制(监听器)【ApplicationEvent  ApplicationListenerSpring框架中事件机制(模型)非常多,比如各种监听器,第三方框架整合Spring时的事件触发(阿波罗)等

8、事件驱动模型案例

改造观察者模式,使用Spring事件机制模型。Spring事件机制中主要包含三要素:

  • 事件源(ApplicationEvent
  • 监听器(ApplicationListener
  • 发布事件者(ApplicationEventPublisher以及ApplicationEventMulticaster

简要说明:

ApplicationEvent事件源是我们自定义的,ApplicationListener监听器是注册在Spring容器中的。ApplicationEventPublisher中ApplicationEventMulticaster类具备发布功能,当事件发布者发布事件之后,监听器监听到事件源就会执行相应的逻辑。

其中ApplicationListener相当于观察者模式中的观察者(Observe)。事件源相当于主题(Subject)。区别在于事件不会主动通知观察者,而是通过监听器这个中间介质来将两者关联起来的。

事件发布者我们可以自定义,也可以不定义。直接使用spring中的ApplicationContext来发布事件,该类继承了ApplicationEventPublisher类,具备发布事件的功能。

定义事件:

/**
 * 委托方下单事件
 *
 * @author: ws
 * @date: 2020/9/21 15:12
 */
public class BosOrderEvent extends ApplicationContextEvent {

    private String eventName;

    private String msg;

    public BosOrderEvent(ApplicationContext source, String eventName, String msg) {
        super(source);
        this.eventName = eventName;
        this.msg = msg;
    }

    public String getEventName() {
        return eventName;
    }

    public void setEventName(String eventName) {
        this.eventName = eventName;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

定义物流系统监听器:

/**
 * 物流监听
 *
 * @author: ws
 * @date: 2020/9/21 15:20
 */
public class WlListener implements ApplicationListener<BosOrderEvent>{

    @Override
    public void onApplicationEvent(BosOrderEvent event) {
        System.out.println("物流系统监控到:【"+event.getEventName()+"】事件被触发,通知消息为:"+event.getMsg());
    }
}

定义库存系统监听器:

/**
 * 库存监听器
 *
 * @author: ws
 * @date: 2020/9/21 15:20
 */
public class StockListener implements ApplicationListener<BosOrderEvent>{

    @Override
    public void onApplicationEvent(BosOrderEvent event) {
        System.out.println("库存系统监控到:【"+event.getEventName()+"】事件被触发,通知消息为:"+event.getMsg());
    }
}

事件发布类:

/**
 * 事件发布类
 *
 * @author: ws
 * @date: 2020/9/21 15:26
 */
public class BosOrderPublish implements ApplicationEventPublisherAware {

    // 获取Spring中的发布事件对象
    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void publishEvent(ApplicationEvent event){
        System.out.println("发布事件被调用。。。。。");
        applicationEventPublisher.publishEvent(event);
    }
}

配置类:

/**
 * 委托方下单配置类
 *
 * @author: ws
 * @date: 2020/9/21 15:35
 */
@Configuration
public class BosOrderConfig {

    @Bean
    public WlListener wlListener(){
        return new WlListener();
    }

    @Bean
    public StockListener stockListener(){
        return new StockListener();
    }

    @Bean
    public BosOrderPublish bosOrderPublish(){
        return new BosOrderPublish();
    }

}

测试类:

public class BosOrderTest {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(BosOrderConfig.class);
        BosOrderEvent bosOrderEvent = new BosOrderEvent(context, "bos下单", "车辆更新");
        // context.publishEvent(bosOrderEvent);
        BosOrderPublish bosOrderPublish = (BosOrderPublish)context.getBean("bosOrderPublish");
        bosOrderPublish.publishEvent(bosOrderEvent);
    }
}

运行结果:

UML类图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值