观察者模式

简介: 观察者模式

什么是观察者模式



观察者一般可以看做是第三者,比如在学校上自习的时候,大家肯定都有过交头接耳、各种玩耍的经历,这时总会有一个“放风”的小伙伴,当老师即将出现时及时“通知”大家老师来了。再比如,拍卖会的时候,大家相互叫价,拍卖师会观察最高标价,然后通知给其它竞价者竞价,这就是一个观察者模式。


  对于观察者模式而言,肯定有观察者和被观察者之分。比如在一个目录下建立一个文件,这时系统会通知目录管理器增加目录,并通知磁盘减少空间,在这里,文件就是观察者,目录管理器和磁盘就是被观察者。


  观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。UML结构图如下:


image.png

13931e0b5e88e1dfb6961f3dc62e1d9.png


其中,Subject类是主题,它把所有对观察者对象的引用文件存在了一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供了一个接口,可以增加和删除观察者对象;Observer类是抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己;ConcreteSubject类是具体主题,将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知;ConcreteObserver是具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协同。


主题Subject


首先定义一个观察者数组,并实现增、删及通知操作。它的职责很简单,就是定义谁能观察,谁不能观察,用CopyOnWriteArrayList是线程同步的,比较安全,也可以使用ArrayList,是线程异步的,但不安全。

public class Subject {
    //观察者数组
    private CopyOnWriteArrayList<Observer> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
    //增加一个观察者
    public void addObserver(Observer observer) {
        this.copyOnWriteArrayList.add(observer);
    }
    //删除一个观察者
    public void deleteObserver(Observer observer) {
        this.copyOnWriteArrayList.remove(observer);
    }
    //通知所有观察者
    public void notifyObserver() {
        copyOnWriteArrayList.forEach(item -> {
            item.update();
        });
    }
}


抽象观察者



观察者一般是一个接口,每一个实现该接口的实现类都是具体观察者。

public interface Observer {
    //更新
    void update();
}


具体主题



继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种

public class ConcreteSubject extends Subject {
    //具体业务
    public void doSomething(){
        //...
        super.notifyObserver();
    }
}


具体观察者


public class ConcreteObserver implements Observer{
    @Override
    public void update() {
        System.out.println("收到消息,进行处理");
    }
}
//客户端
public class Client {
    public static void main(String[] args) {
        //创建一个主题
        ConcreteSubject subject = new ConcreteSubject();
        //定义一个观察者
        Observer observer=new ConcreteObserver();
        //观察
        subject.addObserver(observer);
        subject.doSomething();
    }
}


观察者模式的实现



下面举一个具体实例,假设上班时间有一部分同事在看股票,一部分同事在看NBA,这时老板回来了,前台通知了部分同事老板回来了,这些同事及时关闭了网页没被发现,而没被通知到的同事被抓了个现行,被老板亲自“通知”关闭网页,UML图如下:


image.png

image.png


通知者接口


public interface SubjectDemo {
    //增加
    void attach(ObserverDemo observerDemo);
    //删除
    void detach(ObserverDemo observerDemo);
    //通知
    void notifyObservers();
    //状态
    void setAction(String action);
    String getAction();
}


观察者接口


public abstract class ObserverDemo {
    protected String name;
    protected SubjectDemo subjectDemo;
    public ObserverDemo(String name, SubjectDemo subjectDemo) {
        this.name = name;
        this.subjectDemo = subjectDemo;
    }
    public abstract void update();
}


具体通知者


前台Secretary和老板Boss作为具体通知者,实现Subject接口。这里只给出Secretary类的代码,Boss类与之类似。

public class Secretary implements SubjectDemo {
    //同事列表
    private List<ObserverDemo> observerDemos = new CopyOnWriteArrayList<>();
    private String action;
    @Override
    public void attach(ObserverDemo observerDemo) {
        observerDemos.add(observerDemo);
    }
    @Override
    public void detach(ObserverDemo observerDemo) {
        observerDemos.remove(observerDemo);
    }
    @Override
    public void notifyObservers() {
        observerDemos.forEach(item -> {
            item.update();
        });
    }
    @Override
    public void setAction(String action) {
        this.action = action;
    }
    @Override
    public String getAction() {
        return action;
    }
}


具体观察者


public class StockObserver extends ObserverDemo {
    public StockObserver(String name, SubjectDemo subjectDemo) {
        super(name, subjectDemo);
    }
    @Override
    public void update() {
        System.out.println(subjectDemo.getAction()+"\n"+name+"关闭股票行情,继续工作");
    }
}
public class NBAObserver extends ObserverDemo {
    public NBAObserver(String action, SubjectDemo subjectDemo) {
        super(action, subjectDemo);
    }
        @Override
        public void update () {
            System.out.println(subjectDemo.getAction() + "\n" + name + "关闭直播,继续工作");
        }
    }


前台作为通知者进行通知


前台作为通知者,通知观察者。这里添加adam和tom到通知列表,并从通知列表中删除了adam,测试没在通知列表中的对象不会收到通知。

public class TestDemo {
    public static void main(String[] args) {
        //前台为通知者
        Secretary secretary = new Secretary();
        StockObserver observer = new StockObserver("adam", secretary);
        NBAObserver observer1 = new NBAObserver("tom", secretary);
        //前台通知
        secretary.attach(observer);
        secretary.attach(observer1);
        //adam没有被前台通知到,所以被老板抓个现行
        secretary.detach(observer);
        //老板回来了
        secretary.setAction("小心!Boss回来了!");
        //发通知
        secretary.notifyObservers();
    }
}



目录
打赏
0
0
0
0
4
分享
相关文章
数字图像处理实验(五)|图像复原{逆滤波和伪逆滤波、维纳滤波deconvwnr、大气湍流扰动模型、运动模糊处理fspecial}(附matlab实验代码和截图)
数字图像处理实验(五)|图像复原{逆滤波和伪逆滤波、维纳滤波deconvwnr、大气湍流扰动模型、运动模糊处理fspecial}(附matlab实验代码和截图)
1395 0
数字图像处理实验(五)|图像复原{逆滤波和伪逆滤波、维纳滤波deconvwnr、大气湍流扰动模型、运动模糊处理fspecial}(附matlab实验代码和截图)
AppAgentX:告别重复点击!自我进化式GUI代理自动生成高级操作,效率翻倍
AppAgentX 是西湖大学推出的新型自我进化式 GUI 代理框架,通过记忆和进化机制提升智能手机交互的效率和智能性,支持复杂任务和跨应用操作,显著优于现有方法。
262 0
微服务架构下的接口限流策略与实践#### 一、
本文旨在探讨微服务架构下,面对高并发请求时如何有效实施接口限流策略,以保障系统稳定性和服务质量。不同于传统的摘要概述,本文将从实际应用场景出发,深入剖析几种主流的限流算法(如令牌桶、漏桶及固定窗口计数器等),通过对比分析它们的优缺点,并结合具体案例,展示如何在Spring Cloud Gateway中集成自定义限流方案,实现动态限流规则调整,为读者提供一套可落地的实践指南。 #### 二、
189 3
《数据主权:人工智能时代的核心基石与挑战》
在数字化时代,人工智能成为社会变革的强大力量,深刻改变着我们的生活方式。数据主权作为其核心基石,涉及国家、企业和个人的数据管辖与控制权。国家层面,数据主权关乎国家安全与经济竞争力;企业层面,合规利用数据可提升竞争力,但也面临法律风险;个人层面,隐私保护至关重要。国际社会正通过法规和技术手段(如GDPR和区块链)应对这些挑战,以确保数据安全与隐私,推动人工智能健康发展。
176 18
Flink CDC MySQL同步MySQL错误记录
在使用Flink CDC同步MySQL数据时,常见的错误包括连接错误、权限错误、表结构变化、数据类型不匹配、主键冲突和
345 17
怎么保证后端服务稳定性,怎么做容灾
【10月更文挑战第28天】保证后端服务稳定性及做好容灾措施是一个系统工程,需要从多个方面进行考虑和实施
docker build -t和docker build -f区别
参数用于指定要使用的Dockerfile的路径,允许你在不同的位置使用不同的Dockerfile来构建镜像。
250 0
IDEA 2022 之 Lombok 使用 教程
IDEA 2022 之 Lombok 使用 教程
1067 0
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等