设计模式-观察者模式

一.观察者模式

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

**意图:**定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

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

**何时使用:**一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

**如何解决:**使用面向对象技术,可以将这种依赖关系弱化。

**关键代码:**在抽象类里有一个 ArrayList 存放观察者们。

应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。

优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。

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

使用场景:

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象......,可以使用观察者模式创建一种链式触发机制。

注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

观察者模式包含以下几个核心角色:

  • 主题(Subject):也称为被观察者或可观察者,它是具有状态的对象,并维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。
  • 观察者(Observer):观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。
  • 具体主题(Concrete Subject):具体主题是主题的具体实现类。它维护着观察者列表,并在状态发生改变时通知观察者。
  • 具体观察者(Concrete Observer):具体观察者是观察者的具体实现类。它实现了更新方法,定义了在收到主题通知时需要执行的具体操作。

观察者模式通过将主题和观察者解耦,实现了对象之间的松耦合。当主题的状态发生改变时,所有依赖于它的观察者都会收到通知并进行相应的更新。

二.demo

1.事件监听接口

java 复制代码
public interface EventListener {
    void doEvent(LotteryResult result);
}

2.实现监听接口(短信、MQ)

java 复制代码
public class MessageEventListener implements EventListener {
    private Logger logger = LoggerFactory.getLogger(MessageEventListener.class);

    @Override
    public void doEvent(LotteryResult result) {
        logger.info("给⽤户 {} 发送短信通知(短信):{}", result.getuId(),
                result.getMsg());
    }
}
java 复制代码
public class MQEventListener implements EventListener {
    private Logger logger = LoggerFactory.getLogger(MQEventListener.class);

    @Override
    public void doEvent(LotteryResult result) {
        logger.info("记录⽤户 {} 摇号结果(MQ):{}", result.getuId(),
                result.getMsg());
    }
}

3.事件管理类

java 复制代码
public class EventManager {
    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 enum EventType {
        MQ, Message
    }

    /**
     * 订阅
     *
     * @param eventType 事件类型
     * @param listener  监听
     */
    public void subscribe(Enum<EventType> eventType, EventListener
            listener) {
        List<EventListener> users = listeners.get(eventType);
        users.add(listener);
    }

    /**
     * 取消订阅
     *
     * @param eventType 事件类型
     * @param listener  监听
     */
    public void unsubscribe(Enum<EventType> eventType, EventListener
            listener) {
        List<EventListener> users = listeners.get(eventType);
        users.remove(listener);
    }

    /**
     * 通知
     *
     * @param eventType 事件类型
     * @param result    结果
     */
    public void notify(Enum<EventType> eventType, LotteryResult result) {
        List<EventListener> users = listeners.get(eventType);
        for (EventListener listener : users) {
            listener.doEvent(result);
        }
    }
}

整个处理的实现上提供了三个主要⽅法;订阅( subscribe )、取消订阅( unsubscribe )、通知 ( notify )。这三个⽅法分别⽤于对监听时间的添加和使⽤。

另外因为事件有不同的类型,这⾥使⽤了枚举的⽅式进⾏处理,也⽅便让外部在规定下使⽤事件, ⽽不⾄于乱传信息( EventType.MQ 、 EventType.Message )。

4.业务抽象类接⼝

java 复制代码
public abstract class LotteryService {
    private EventManager eventManager;

    public LotteryService() {
        eventManager = new EventManager(EventManager.EventType.MQ,
                EventManager.EventType.Message);
        eventManager.subscribe(EventManager.EventType.MQ, new
                MQEventListener());
        eventManager.subscribe(EventManager.EventType.Message, new
                MessageEventListener());
    }

    public LotteryResult draw(String uId) {
        LotteryResult lotteryResult = doDraw(uId);
        // 需要什么通知就给调⽤什么⽅法
        eventManager.notify(EventManager.EventType.MQ, lotteryResult);
        eventManager.notify(EventManager.EventType.Message,
                lotteryResult);
        return lotteryResult;
    }

    protected abstract LotteryResult doDraw(String uId);
}

5.业务接⼝实现类

java 复制代码
public class LotteryServiceImpl extends LotteryService {
    private MinibusTargetService minibusTargetService = new
            MinibusTargetService();

    @Override
    protected LotteryResult doDraw(String uId) {
        // 摇号
        String lottery = minibusTargetService.lottery(uId);
        // 结果
        return new LotteryResult(uId, lottery, new Date());
    }
}

6.测试

java 复制代码
@Test
public void test(){
        LotteryService lotteryService=new LotteryServiceImpl();
        LotteryResult result=lotteryService.draw("2765789109876");
        logger.info("测试结果:{}",JSON.toJSONString(result));
        }

参考:观察者模式 | 菜鸟教程

《重学Java设计模式》

相关推荐
还是大剑师兰特10 小时前
vue源代码采用的设计模式分解
javascript·vue.js·设计模式
数智研发说17 小时前
AI技术下研发体系重构
人工智能·科技·设计模式·重构·交互·设计规范
今日上上签070719 小时前
《OmniMeetProTrack 全维会议链智能追录系统 软件设计文档》
人工智能·设计模式·aigc·软件工程·团队开发·需求分析·规格说明书
找了一圈尾巴1 天前
设计模式(结构型)-组合模式
设计模式·组合模式
编码小笨猪1 天前
[ 设计模式 ] | 单例模式
c++·单例模式·设计模式
ValidationExpression1 天前
设计模式-工厂模式
python·设计模式
晓风残月淡2 天前
系统架构设计师:设计模式——结构型设计模式
设计模式·系统架构
Wabi_sabi_x2 天前
C++设计模式:面向对象的八大设计原则之一
开发语言·c++·设计模式
智驾3 天前
C++,设计模式,【建造者模式】
c++·设计模式·建造者模式