【浅谈设计模式】(18):观察者模式 | 动物干饭案例快速入门

前言

今天,学习观察者模式。

一、概述

1.1 引入

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

ok,观察者模式又称为发布-订阅模式。

既有观察者,也有被观察者。

当被观察者产生某种行为之后,就会通知观察者执行某种操作。

举几个场景:

  • 1、当保安 (观察者) 发现店里有人 (被观察者) 偷东西(事件),就会出手干预;
  • 2、当用户(观察者)关注了潇雷公众号(被观察者),如果有推送消息(事件),就会接收到;
  • 3、当老婆(观察者)发现老公(被观察者)发工资(事件),可能就会开始上交模式;
  • ...

1.1 结构

OK, 观察者相对来说容易列举的场景会有很多,也很容易理解,现在我们要再次定义下观察者模式拥有的角色。

上面可能已经发现了,至少存在两个角色-观察者与被观察者。

对应到面向对象中,因为要解耦,所以又抽象出,具体观察者和具体被观察者,四个对象啦。

  • Subject 被观察者:

    • 抽象被观察者,定义了被观察者的事件(方法),它必须要能够动态的新增和取消观察者。里面维护了一个所有观察者对象的集合。
  • Observer 观察者:

    • 抽象观察者,是观察者的抽象类,定义了一个接收方法,当被观察者发生变化时,就会执行这个观察者方法
  • ConcrectSubject:

    • 具体被观察者:实现具体被观察者的事件方法。
  • ConcrectObserver:

    • 具体观察者:实现抽象观察者定义的接收方法。

二、案例实现

2.1 案例引入

这边也举个简单的场景。

农夫家里养了很多动物,鸡、鸭、猪等

每次到饭点的时候,农夫就会进入围栏里喊它们,动物一观察到这个举动就立马跑过来。

抽象成角色模型,大概是下面这种。

2.2 代码引入

抽象观察者:

csharp 复制代码
public interface Observer {
    void subscript();
}

具体观察者:

typescript 复制代码
public class AnimalObserver implements Observer{
    private String name;
    public AnimalObserver(String name){
        this.name = name;
    }

    @Override
    public void subscript() {
        System.out.println(name+"看到主人进来,跑过去干饭");
    }
}

被观察者:

csharp 复制代码
public interface Subject {
    // 添加观察者
void addObserver(Observer observer);
    // 删除观察者
void deleteObserver(Observer observer);
    // 通知方法
void notifyObserver();
}

具体被观察者:

typescript 复制代码
public class OwnerSubject implements Subject{
    private List<Observer> observers = new ArrayList<>();

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

    @Override
    public void deleteObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObserver() {
        for (Observer observer : observers) {
            observer.subscript();
        }
    }
}

客户端测试

ini 复制代码
public class Client {
    public static void main(String[] args) {
        OwnerSubject ownerSubject = new OwnerSubject();

        AnimalObserver observer = new AnimalObserver("猪猪");
        AnimalObserver observer2 = new AnimalObserver("母鸡");
        AnimalObserver observer3 = new AnimalObserver("鸭子");
        ownerSubject.addObserver(observer);
        ownerSubject.addObserver(observer2);
        ownerSubject.addObserver(observer3);

        // 主人进来这个动作,被这些订阅者观察到
ownerSubject.notifyObserver();

    }
}

打印输出:

2.3 spring Event 引入

其实观察者模式,跟发布-订阅模型非常相似。

很多中间件都提供了这种模式,消费者就是观察者的角色。而Spring 中我们常常想实现观察者模式,不会说自己来手写,更多的是采用 Spring Event 基于事件解耦的方式来完成。

Spring Event 将发布者和消费者分离开,实现代码的解耦,让程序的代码更加职责明确清晰。同时,Spring Event 支持异步事件处理,提高系统的并发处理能力。

对应三个角色:

  • 事件(Event):事件是个普通的对象,封装关于事件的信息

  • 事件发布者:触发发布事件

  • 事务监听者:监听负责响应特定类型事件的组件,并定义了事件发生时需要执行的逻辑。

那如何用 Spring 来实现我们上述的这个例子呢?

  • 发布者:
typescript 复制代码
@Service
public class OwnerPublish {
    @Autowired
    private ApplicationContext applicationContext;


    public void publish(){
        applicationContext.publishEvent(new FoodEvent("来干饭!"));
    }
}
  • 事件:
scala 复制代码
public class FoodEvent extends ApplicationEvent {
    public FoodEvent(Object source) {
        super(source);
    }
}
  • 消费者
csharp 复制代码
@Component
public class AnimalEventListener {
    @EventListener(FoodEvent.class)
    public void onApplicationEvent(FoodEvent event){
        Object source = event.getSource();
        System.out.println("猪猪:"+source);
    }
}
less 复制代码
@Component
public class Animal2EventListener {
    @EventListener(FoodEvent.class)
    @Async
    public void onApplicationEvent(FoodEvent event){
        Object source = event.getSource();
        System.out.println("大鹅:"+source);
    }
}

客户端:

typescript 复制代码
@Autowired
OwnerPublish ownerPublishl;
@Test
public void sendMsg(){
    ownerPublishl.publish();
}

发布订阅模式与观察者模式还是有区别的。

  • 观察者模式中,观察者是知道有哪些被观察者的,就像小动物知道有主人这个记录。

  • 而发布订阅模式中,发布者和订阅者,是通过事件解耦的,都不需要知道对方的存在。

总结

观察者模式实现了解耦,定义了一种一对多的依赖关系,多个观察者同时监听者某个对象,当对象触发某动作后,多个观察者就会同时执行某种操作。同时被观察者必须要维护有哪些观察者的列表,这是与发布-订阅模式的区别所在,所以类似微信公众号发文,维护了一个粉丝列表,这些人可以同时接收到对应信息。

何时使用观察者模式?

  • 想抽离业务解耦代码的时候
  • 当多个对象依赖同一个对象方法的时候
  • 还可以通过异步处理方法,提高程序的性能。
相关推荐
丶白泽17 分钟前
重修设计模式-设计原则
设计模式·接口隔离原则·依赖倒置原则·开闭原则
【D'accumulation】20 分钟前
典型的MVC设计模式:使用JSP和JavaBean相结合的方式来动态生成网页内容典型的MVC设计模式
java·设计模式·mvc
wn53130 分钟前
【Go - 类型断言】
服务器·开发语言·后端·golang
试行35 分钟前
Android实现自定义下拉列表绑定数据
android·java
茜茜西西CeCe40 分钟前
移动技术开发:简单计算器界面
java·gitee·安卓·android-studio·移动技术开发·原生安卓开发
救救孩子把1 小时前
Java基础之IO流
java·开发语言
小菜yh1 小时前
关于Redis
java·数据库·spring boot·redis·spring·缓存
宇卿.1 小时前
Java键盘输入语句
java·开发语言
浅念同学1 小时前
算法.图论-并查集上
java·算法·图论
希冀1231 小时前
【操作系统】1.2操作系统的发展与分类
后端