Java设计模式中的观察者模式

 更新时间:2023年02月16日 15:27:31   作者:非凡的小笨鱼  
观察者模式定义对象之间的一种一对多的依赖关系,使得每当一个对象的状态发生变化时,其相关的依赖对象都可以得到通知并被自动更新。主要用于多个不同的对象对一个对象的某个方法会做出不同的反应

一.介绍

观察者模式(Observer Pattern)属于行为型模式。定义了对象之间的一对多依赖,让多个观察者同时监听某一个主题对象,类似于广播机制,只需要分发广播,感兴趣的对象自动接收该广播。我们平常所说的Observer、Listener、Hook、Callback都和这个模式有关

二.场景约束

小孩(Baby)哭的时候会通知到爸爸(Dad)和妈妈(Mum),爸爸妈妈会对此采取不同的行为

三.UML类图

版本一

版本二

新增事件类将观察者与主题解耦,观察者可以根据不同的事件执行不同的操作,也可以直接对事件源进行操作

四.示意代码(版本一)

业务代码

//抽象观察者
public interface Observer {
    void action();
}
//抽象主题
abstract class Subject{
    protected List<Observer> observers = new ArrayList<>();
    public void addObserver(Observer observer) {
        observers.add(observer);
    }
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }
    public abstract void notifyObserver();
}
//具体主题-被观察者
class Baby extends Subject{
    @Override
    public void notifyObserver() {
        System.out.println("baby cry");
        observers.forEach(Observer::action);
    }
}
//具体观察者
class Dad implements Observer {
    @Override
    public void action() {
        System.out.println("dad feed baby");
    }
}
//具体观察者
class Mum implements Observer {
    @Override
    public void action() {
        System.out.println("mum hug baby");
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        Baby baby = new Baby();
        baby.addObserver(new Mum());
        baby.addObserver(new Dad());
        baby.notifyObserver();
    }
}

五.示意代码(版本二)

//抽象观察者
public interface Observer {
    void action(Event event);
}
//抽象主题
abstract class Subject {
    protected List<Observer> observers = new ArrayList<>();
    public void addObserver(Observer observer) {
        observers.add(observer);
    }
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }
    public abstract void notifyObserver();
}
//具体主题-被观察者
class Baby extends Subject {
    @Override
    public void notifyObserver() {
        System.out.println("baby cry");
        CryEvent cryEvent = new CryEvent(new Date().getTime(), this);
        observers.forEach(observer -> observer.action(cryEvent));
    }
}
//抽象事件
abstract class Event {
    public abstract Object getSource();
}
//具体事件
class CryEvent extends Event {
    public long when;
    Baby source;
    public CryEvent(long when, Baby source) {
        this.when = when;
        this.source = source;
    }
    @Override
    public Baby getSource() {
        return source;
    }
}
//具体观察者
class Dad implements Observer {
    @Override
    public void action(Event event) {
        System.out.println("dad feed baby");
        if(event.getSource() instanceof Baby){
            System.out.println("dad对事件源进行处理");
        }
    }
}
//具体观察者
class Mum implements Observer {
    @Override
    public void action(Event event) {
        System.out.println("mum hug baby");
        if(event.getSource() instanceof Baby){
            System.out.println("mum对事件源进行处理");
        }
    }
}

客户端

public class Client {
    public static void main(String[] args) {
       Baby baby = new Baby();
       baby.addObserver(new Dad());
       baby.addObserver(new Mum());
       baby.notifyObserver();
    }
}

六.观察者模式与发布订阅模式

发布订阅模式

  • 发布者不会直接通知订阅者
  • 发布者与订阅者完全解耦

观察者模式

  • 主题要自己通知(notify)观察者
  • 主题与观察者松耦合

七.优点

  • 符合依赖倒置原则(观察者与主题都依赖于抽象)
  • 降低耦合(主题与观察者之间的耦合关系)

八.在JDK中的典型应用

在java.awt包下有很多观察者模式的身影,先来看下简单的UML类图

主题角色:java提供的组件类(以Button为例) 观察者角色:java提供的事件监听(各种Listener) 事件角色:鼠标事件、键盘事件等等

再来看看一个小demo 在窗口中添加一个按钮,给按钮添加上鼠标与键盘的相关事件,当点击按钮或者按下键盘的时候在控制台打印相应的语句

public class MainFrame extends JFrame {
    public MainFrame() throws HeadlessException {
        //定义一个具体主题
        Button button = new Button("click");
        //给主题添加观察者(鼠标监听)
        button.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                //MouseEvent就是具体事件
                System.out.println("按钮被点击");
            }
        });
        //给主题添加观察者(键盘监听)
        button.addKeyListener(new KeyAdapter() {
            @Override
            public void keyTyped(KeyEvent e) {
                //KeyEvent就是具体事件
                System.out.println("按下" + e.getKeyChar());
            }
        });
        add(button);
        setSize(100,100);
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }
    public static void main(String[] args) {
        new MainFrame();
    }
}

这里的KeyAdapter与MouseAdapter使用的是适配器模式

到此这篇关于Java设计模式中的观察者模式的文章就介绍到这了,更多相关Java观察者模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解SpringBoot中关于%2e的Trick

    详解SpringBoot中关于%2e的Trick

    这篇文章主要介绍了SpringBoot中关于%2e的Trick,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • Spring实现跨域的几种方式小结

    Spring实现跨域的几种方式小结

    这篇文章主要给大家总结了几种Spring实现跨域的方式,文中通过代码示例介绍的非常详细,对我们的学习活工作有一定的帮助,需要的朋友可以参考下
    2023-07-07
  • SpringBoot Application的exclude不生效问题及排查

    SpringBoot Application的exclude不生效问题及排查

    这篇文章主要介绍了SpringBoot Application的exclude不生效问题及排查,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • 浅谈Java 代理机制

    浅谈Java 代理机制

    Java 有两种代理方式,一种是静态代理,另一种是动态代理。如果我们在代码编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;如果不能确定,那么可以使用类的动态加载机制,在代码运行期间加载被代理的类这就是动态代理
    2021-06-06
  • MyBatis Generator生成数据库模型实现示例

    MyBatis Generator生成数据库模型实现示例

    这篇文章主要为大家介绍了MyBatis Generator生成数据库模型实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • java获取文件扩展名的方法小结【正则与字符串截取】

    java获取文件扩展名的方法小结【正则与字符串截取】

    这篇文章主要介绍了java获取文件扩展名的方法,结合实例形式分析了使用正则与字符串截取两种获取扩展名的操作技巧,需要的朋友可以参考下
    2017-01-01
  • 基于Java随机生成手机短信验证码的实例代码

    基于Java随机生成手机短信验证码的实例代码

    这篇文章主要介绍了Java随机生成手机短信验证码的实例代码,代码分为哦简单版和复杂版,需要的朋友可以参考下
    2019-04-04
  • Junit测试多线程无法得到结果的问题解决

    Junit测试多线程无法得到结果的问题解决

    在测试一个文件转换工具类的时候,发生一个有趣的现象,同样的输入,使用Main函数可以正确解析,得到结果,使用Junit却无法得到结果,神奇的是,即使捕获Throwable,也无法捕获到仍和异常。
    2021-05-05
  • MyBatis Mapper XML中比较操作符转义问题解决

    MyBatis Mapper XML中比较操作符转义问题解决

    在使用MyBatis编写Mapper XML时,有时会遇到比较操作符需要进行转义的情况,本文主要介绍了MyBatis Mapper XML中比较操作符转义问题解决,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • 使用feign服务调用添加Header参数

    使用feign服务调用添加Header参数

    这篇文章主要介绍了使用feign服务调用添加Header参数的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06

最新评论