设计模式-行为型模式-观察者模式

行为模式包含:观察者模式,模板方法模式,策略模式,职责链模式,状态模式,命令模式,中介者模式,迭代器模式,访问者模式,备忘录模式,解释器模式。

1.观察者模式的定义

定义对象之间一对多依赖关系,这样当一个对象改变状态时,他的所有依赖项都会自动得到通知和更新;

1.1 观察者模式的优缺点

优点

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系;
  • 被观察者发送通知,所有注册的观察者都会收到信息;

缺点

  • 如果观察者很多的话,所有的观察者收到被观察者发送的通知会耗时;
  • 如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃;

1.2 观察者模式的使用场景

  • 当一个对象状态的改变需要改变其他对象时,如商品库存数量发生变化需要通知商品详情页、购物车等系统改变数量;
  • 发布订阅的场景,如微信公众号、微博、b站等,发布者发布内容,通知便会发送到关注者;
  • 需要创建一种链式触发机制时,如在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象,这样通过观察者模式能够很好的实现;
  • 需要建立基于事件触发的场景,如基于 Java UI 的编程,所有键盘和鼠标事件都由它的侦听器对象和指定函数处理。当用户单击鼠标时,订阅鼠标单击事件的函数将被调用,并将所有上下文数据作为方法参数传递给它;

2.观察者模式原理

  • 抽象被观察者(Subject):把所有观察者对象保存在一个集合里,每个被观察者都可以有任意数量的观察者,抽象被观察者提供一个接口,可以增加和删除观察者对象;
  • 具体被观察者(ConcreteSubject):将有关状态存入具体观察者对象,在具体被观察者的内部状态发生改变时,给所有注册过的观察者发送通知;
  • 抽象观察者(Observer):是观察者的抽象类,定义了一个更新接口,使得在得到被观察者更改通知时更新自己;
  • 具体观察者(ConcreteObserver):实现抽象观察者定义的更新接口,以便在得到被观察者更改通知时更新自身的状态,在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要与具体目标一致;

3.观察者模式的实现

【实例】

实现一个买房摇号的程序,摇号结束,需要通过短信告知用户摇号结果,还需要向MQ中保存用户本次摇号的信息。

【代码】

事件监听

java 复制代码
/**
 * 事件监听接口
 **/
public interface EventListener {
    void doEvent(LotteryResult result);
}

/**
 * 短信发送事件
 **/
public class MessageEventListener implements EventListener {

    @Override
    public void doEvent(LotteryResult result) {
        System.out.println("发送短信通知用户ID为: " + result.getuId() +
                ",您的摇号结果如下: " + result.getMsg());
    }
}

/**
 * MQ消息发送事件
 **/
public class MQEventListener implements EventListener {

    @Override
    public void doEvent(LotteryResult result) {
        System.out.println("记录用户摇号结果(MQ), 用户ID:" +  result.getuId() +
                ",摇号结果:" + result.getMsg());
    }
}

事件处理

java 复制代码
/**
 * 事件处理类
 **/
public class EventManager {

    public enum EventType{
        MQ,Message
    }

    //监听器集合
    Map<Enum<EventType>, List<EventListener>> listeners = new HashMap<>();

    public EventManager(Enum<EventType>... operations) {
        for (Enum<EventType> operation : operations) {
            this.listeners.put(operation,new ArrayList<>());
        }
    }

    /**
     * 订阅
     */
    public void subscribe(Enum<EventType> eventType, EventListener listener){
        List<EventListener> users = listeners.get(eventType);
        users.add(listener);
    }

    /**
     * 取消订阅
     */
    public void unsubscribe(Enum<EventType> eventType,EventListener listener){
        List<EventListener> users = listeners.get(eventType);
        users.remove(listener);
    }

    /**
     * 通知
     */
    public void notify(Enum<EventType> eventType, LotteryResult result){
        List<EventListener> users = listeners.get(eventType);
        for (EventListener listener : users) {
            listener.doEvent(result);
        }
    }
}

摇号业务处理

java 复制代码
/**
 * 开奖服务接口
 **/
public abstract class LotteryService{
    private EventManager eventManager;
    public LotteryService(){
        //设置事件类型
        eventManager = new EventManager(EventManager.EventType.MQ, EventManager.EventType.Message);
        //订阅
        eventManager.subscribe(EventManager.EventType.Message,new MessageEventListener());
        eventManager.subscribe(EventManager.EventType.MQ,new MQEventListener());
    }
    public LotteryResult lotteryAndMsg(String uId){
        LotteryResult result = lottery(uId);
        //发送通知
        eventManager.notify(EventManager.EventType.Message,result);
        eventManager.notify(EventManager.EventType.MQ,result);
        return result;
    }
    public abstract LotteryResult lottery(String uId);
}
/**
 * 开奖服务
 **/
public class LotteryServiceImpl extends LotteryService {
    //注入摇号服务
    private DrawHouseService houseService = new DrawHouseService();
    @Override
    public LotteryResult lottery(String uId) {
        //摇号
        String result = houseService.lots(uId);
        return new LotteryResult(uId,result,new Date());
    }
}

测试

java 复制代码
@Test
public void test2(){
    LotteryService ls = new LotteryServiceImpl();
    LotteryResult result  = ls.lotteryAndMsg("1234567887654333");
    System.out.println(result);
}

4.JDK 中对观察者模式的支持

JDK中提供了Observable类以及Observer接口,它们构成了JDK对观察者模式的支持;

  • java.util.Observer 接口: 该接口中声明了一个方法,它充当抽象观察者,其中声明了一个update方法.

    复制代码
    void update(Observable o, Object arg);
  • java.util.Observable 类: 充当观察目标类(被观察类) , 在该类中定义了一个Vector集合来存储观察者对象.下面是它最重要的 3 个方法。

    • void addObserver(Observer o) 方法:用于将新的观察者对象添加到集合中。
    • void notifyObservers(Object arg) 方法:调用集合中的所有观察者对象的 update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。

    • void setChange() 方法:用来设置一个 boolean 类型的内部标志,注明目标对象发生了变化。当它为true时,notifyObservers() 才会通知观察者。

用户可以直接使用Observer接口和Observable类作为观察者模式的抽象层,再自定义具体观察者类和具体观察目标类,使用JDK中提供的这两个类可以更加方便的实现观察者模式.

相关推荐
晨米酱8 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机13 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机14 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机14 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机14 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤15 小时前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴2 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
李广坤2 天前
工厂模式
设计模式