Java 设计模式 之 观察者模式(Observer)

本文详细介绍了观察者模式的概念、特点及其实现方式。通过实例演示了如何使用该模式建立对象间的一对多依赖关系,使得当一个对象的状态发生改变时,所有依赖于它的对象都能得到通知并自动更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

观察者模式 简述

编码实现

番外版


观察者模式 简述

1、观察者模式又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

2、观察者模式是类和类之间的关系,不涉及到继承。

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

 

MySubject 类是主对象,Observer1(观察者1)和 Observer2 (观察者1)依赖于 MySubject 的对象,当 MySubject 变化时,Observer1 和 Observer2 必然变化。

AbstractSubject 类中定义着需要监控的对象列表,可以对其进行修改:增加或删除被监控对象,且当 MySubject 变化时,负责通知在列表内存在的对象

编码实现

1、下面使用代码实现上图,假如让一个人(Person)作为被观察对象,一只猫(Cat)和一只狗(Dog)作为观察者,当人开始吃饭时,猫和狗就会收到通知,知道它们的主人开始吃饭了。

2、观察者如下:

package com.lct.observerModel;

//观察者-设计成接口,所有的实现类都是观察者
public interface Observer {
    /**
     * 监控对象发生变化后,此方法用于收到通知,然后自己进行相应的处理
     *
     * @param message :接收的通知信息,如果需要接收发布者的信息,则可以加上,否则可以取消
     */
    public void update(String message);
}
package com.lct.observerModel;

import java.util.logging.Logger;

//猫---实现 Observer 后,自己也是观察者
public class Cat implements Observer {
    @Override
    public void update(String message) {
        Logger logger = Logger.getAnonymousLogger();
        logger.info("汤姆猫:我看到主人准备吃 " + message + " ,我也开始吃猫粮...");
    }
}
package com.lct.observerModel;

import java.util.logging.Logger;

//狗---实现 Observer 后,自己也是观察者
public class Dog implements Observer {
    @Override
    public void update(String message) {
        Logger logger = Logger.getAnonymousLogger();
        logger.info("哮天犬:我看到主人准备吃 " + message + " ,我也开始吃狗粮...");
    }
}

3、被观察者/发布者如下:

package com.lct.observerModel;

//主题---设计成接口,制定规范
// Subject 与 Observer 是两条不同的线,通过 Subject(主题)添加、删除观察者、以及通知观察者等
public interface Subject {
    public void addObserver(Observer observer);//添加观察者

    public void delObserver(Observer observer);//剔除观察者

    /**
     * 通知所有观察者
     *
     * @param message :发送给观察者的信息,如果对方不需要接收信息,则可以取消
     */
    public void notifyObservers(String message);

    /**
     * 上面是对观察者的操作,下面可以定义监控对象(主题)自身的操作
     *
     * @param food 食物名称
     */
    public void eat(String food);
}
package com.lct.observerModel;

import java.util.Iterator;
import java.util.List;
import java.util.Vector;

/**
 * 主题(被监控对象)抽象类,实现 主题接口
 * 主要是显得层次更加清晰,所以采用 接口 -> 抽象类 -> 具体类 的结构
 * 接口中定义所有规范,抽象类中实现所有对观察者的操作,主题具体的操作交由自己的子类去实现
 */
public abstract class AbstractSubject implements Subject {
    private List<Observer> observerVector = new Vector<>();

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

    @Override
    public void delObserver(Observer observer) {
        observerVector.remove(observer);
    }

    @Override
    public void notifyObservers(String message) {
        //通知所有观察者
        Iterator<Observer> observerIterator = observerVector.iterator();
        while (observerIterator.hasNext()) {
            observerIterator.next().update(message);//通知的同时传递信息
        }
    }
}
package com.lct.observerModel;

import java.util.logging.Logger;

//主题具体的实现类,当然还可以有其它类型的子类,都实现自己 eat 方法即可
public class Person extends AbstractSubject {
    @Override
    public void eat(String food) {
        Logger logger = Logger.getAnonymousLogger();
        /**
         * 在自己的执行之前通知所有观察者,则观察者先执行
         * 同理放在自己的执行之后通知所有观察者,则观察者后执行
         */
        notifyObservers(food);//将自己的吃的食物作为信息传递给观察者
        logger.info("杨戬:本君要开始用膳 " + food);
    }
}

4、测试如下:

package com.lct.test;

import com.lct.observerModel.*;

public class Test {
    public static void main(String[] args) {
        Observer observer_cat = new Cat();
        Observer observer_dog = new Dog();

        Subject subject = new Person();
        subject.eat("蚂蚁上树");//此时未添加观察者,所以不会有对象收到通知
        subject.addObserver(observer_cat);
        subject.eat("辣椒炒肉");//此时 cat 会收到通知
        subject.addObserver(observer_dog);
        subject.eat("水煮鱼");//此时 cat 、dog 都会收到通知
    }
}

番外版

1、这种方法相比上面的方式,比较不容易理解,但也是一种方法

2、观察者设计模式三要素:

事件源:触发事件的对象,需要注册监听器

监听器:一般设计成接口,由使用者来实现其中的它想要监听的方法

事件:触发的事件,里面要封装事件源

3、假设现在要监听学生(Student)小明的学习(study)和睡觉(sleep)情况

事件源

package test.publish;

/**
 * Created by Administrator on 2018/7/20 0020.
 * 事件源(观察的目标)----下面监听它的study()、 sleep()方法
 * 需要注册监听器
 */
public class Student {
    /**
     * name:学生的普通属性
     * bookName:学习的书名
     * studentListener:学生监听器
     */
    private String name;
    private String bookName;
    private StudentListener studentListener;

    public void study(String bookName) {
        this.bookName = bookName;
        /** 当注册了监听器时,则实施方法监听
         * 监听器方法中含有事件,事件又封装了事件源,而用户实现了监听器
         * 所以每次此方法一运行,用户会先监听到,而且能从事件中获取事件源(Student)数据*/
        if (studentListener != null) {
            studentListener.studyListener(new StudentEven(this));
        }
        System.out.println("我是 " + name + " ,准备学习 " + bookName);
    }

    public void sleep() {
        /** 当注册了监听器时,则实施方法监听,与上同理*/
        if (studentListener != null) {
            studentListener.sleepListener(new StudentEven(this));
        }
        System.out.println("我是 " + name + " ,准备睡觉!");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getBookName() {
        return bookName;
    }

    /**
     * 注册监听器
     * 只要当注册了监听器,才能监听方法
     *
     * @param studentListener
     */
    public void addStudentListener(StudentListener studentListener) {
        this.studentListener = studentListener;
    }
}

事件

package test.publish;

/**
 * Created by Administrator on 2018/7/20 0020.
 * 事件---封装事件源,便于事件源对象的传入传出
 * 这样的好处是以后用户可以通过触发的事件获取事件源,从而可以获取数据源的数据
 */
public class StudentEven {

    private Object evenSource;

    public StudentEven(Object evenSource) {
        this.evenSource = evenSource;
    }

    public Object getEvenSource() {
        return evenSource;
    }

    public Student getStudent() {
        return (Student) evenSource;
    }
}

监听器

package test.publish;

/**
 * Created by Administrator on 2018/7/20 0020.
 * 监听学生的监听器,通常设计成接口,让用户自己实现
 * 用户可以从 StudentEven 中获取事件源
 */
public interface StudentListener {
    /**
     * 监听器监听的方法
     *
     * @param studentEven 监听器监听的事件
     */
    void studyListener(StudentEven studentEven);

    void sleepListener(StudentEven studentEven);
}

用户监听

package test.publish;

/**
 * Created by Administrator on 2018/7/20 0020.
 * 自己实现监听器,这和使用Java Web的监听器道理是一样的
 * 如果只想监听里面的部分方法,则可以再加适配器
 */
public class MyStudentListener implements StudentListener {
    @Override
    public void studyListener(StudentEven studentEven) {
        Student student = studentEven.getStudent();
        System.out.println("-----------监听到 " + student.getName() + " 开始学习 " + student.getBookName());
    }

    @Override
    public void sleepListener(StudentEven studentEven) {
        Student student = studentEven.getStudent();
        System.out.println("-----------监听到 " + student.getName() + " 开始睡觉");
    }
}

测试结果

/**
 * Created by Administrator on 2018/7/20 0020.
 * 未注册监听器时
 */
public class MyTest {
    public static void main(String[] args) {
        Student student = new Student();
        student.setName("小明同学");
        student.study("论语");
        student.sleep();
    }
}
//输出如下
//我是 小明同学 ,准备学习 论语
//我是 小明同学 ,准备睡觉!
//Process finished with exit code 0
/**
 * Created by Administrator on 2018/7/20 0020.
 * 注册监听后
 */
public class MyTest {
    public static void main(String[] args) {
        Student student = new Student();
        student.setName("小明同学");
        student.addStudentListener(new MyStudentListener());
        student.study("论语");
        student.sleep();
    }
}


/*
-----------监听到 小明同学 开始学习 论语
        我是 小明同学 ,准备学习 论语
        -----------监听到 小明同学 开始睡觉
        我是 小明同学 ,准备睡觉!

        Process finished with exit code 0
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蚩尤后裔-汪茂雄

芝兰生于深林,不以无人而不芳。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值