【设计模式】 观察者模式介绍及C代码实现

简介: 观察者模式(Observer Pattern)是一种常用的设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有观察者都会收到通知并更新自己的状态。观察者模式又称为发布-订阅模式。Subject(主题):被观察的对象,它将所有观察者对象的引用保存在一个集合中,并提供了添加和删除观察者对象的方法。Observer(观察者):观察者接口,定义了更新自己的状态的方法,以便主题在状态发生变化时通知观察者。ConcreteSubject(具体主题)

背景

  在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”,即一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。

  假设有一个简单的应用场景,一个气象站记录当地天气的温度、湿度和气压,并将这些数据展示在一个显示屏上。现在需要实现一个气象站的应用,支持多个显示屏同时显示气象数据,这时候就可以使用观察者模式来实现。

定义

  观察者模式(Observer Pattern)是一种常用的设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有观察者都会收到通知并更新自己的状态。观察者模式又称为发布-订阅模式。

观察者模式的主要角色包括以下几个部分:

Subject(主题):被观察的对象,它将所有观察者对象的引用保存在一个集合中,并提供了添加和删除观察者对象的方法。

Observer(观察者):观察者接口,定义了更新自己的状态的方法,以便主题在状态发生变化时通知观察者。

ConcreteSubject(具体主题):具体的主题实现类,维护一个状态,并在状态发生变化时通知所有观察者。

ConcreteObserver(具体观察者):具体的观察者实现类,实现观察者接口中定义的方法,以便在接收到主题的通知时更新自己的状态。

应用场景

观察者模式常常被用于以下场景:

  1. 一对多的依赖关系:当一个对象的状态发生变化时,需要通知多个对象,并且这些对象需要根据主题对象的状态进行相应的处理,这时可以使用观察者模式。
  2. 发布-订阅模型:观察者模式也被称为发布-订阅模型。在这个模型中,主题对象充当发布者的角色,而观察者充当订阅者的角色。主题对象不需要知道具体的观察者,只需要通知所有的观察者即可。
  3. GUI 事件处理:在 GUI 编程中,经常使用观察者模式来处理用户界面事件。例如,当用户点击按钮时,程序会通知所有的事件监听器来处理该事件。
  4. 网络编程:在网络编程中,经常使用观察者模式来实现异步通信。例如,当客户端连接服务器时,服务器会通知所有的客户端连接已建立。

  总之,当一个对象的状态发生变化时,需要通知多个对象,并且这些对象需要根据主题对象的状态进行相应的处理时,可以使用观察者模式。观察者模式可以帮助我们降低系统的耦合度,增强系统的灵活性和可维护性。

模式结构

在这里插入图片描述

实现步骤

观察者模式的实现步骤如下:

  1. 定义 Subject 接口,该接口定义了注册观察者、删除观察者和通知观察者的方法。
  2. 定义 Observer 接口,该接口定义了观察者需要实现的方法。
  3. 定义具体主题类 ConcreteSubject,实现 Subject 接口,并包含观察者列表。具体主题类负责管理观察者列表,并在状态发生改变时通知观察者。
  4. 定义具体观察者类 ConcreteObserver,实现 Observer 接口,并在 update 方法中更新自己的状态。
  5. 在具体主题类中实现注册观察者、删除观察者和通知观察者的方法。
  6. 在具体观察者类中实现 update 方法,当主题对象的状态发生改变时,观察者会接收到通知并更新自己的状态。
  7. 在客户端代码中创建具体主题对象和具体观察者对象,并将观察者注册到主题对象中。
  8. 当主题对象的状态发生改变时,观察者会接收到通知并更新自己的状态,从而实现观察者模式的功能。

  观察者模式的核心思想是将主题和观察者解耦,使得它们可以独立地变化。主题对象负责管理状态和通知观察者,而观察者对象负责更新自己的状态。观察者模式可以帮助我们降低系统的耦合度,增强系统的灵活性和可维护性。

C语言代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义观察者接口
typedef struct _Observer {
    void (*update)(struct _Observer* self);
} Observer;

// 定义主题接口
typedef struct _Subject {
    void (*registerObserver)(struct _Subject* self, Observer* observer);
    void (*removeObserver)(struct _Subject* self, Observer* observer);
    void (*notifyObservers)(struct _Subject* self);
} Subject;

// 定义具体观察者类
typedef struct _ConcreteObserver {
    Observer base;
    char name[20];
} ConcreteObserver;

// 定义具体主题类
typedef struct _ConcreteSubject {
    Subject base;
    Observer* observers[10];
    int count;
    int state;
} ConcreteSubject;

// 实现具体观察者类的更新方法
void ConcreteObserver_update(Observer* self) {
    ConcreteObserver* observer = (ConcreteObserver*)self;
    printf("%s: state changed\n", observer->name);
}

// 实现具体主题类的注册观察者方法
void ConcreteSubject_registerObserver(Subject* self, Observer* observer) {
    ConcreteSubject* subject = (ConcreteSubject*)self;
    if (subject->count < 10) {
        subject->observers[subject->count++] = observer;
    }
}

// 实现具体主题类的删除观察者方法
void ConcreteSubject_removeObserver(Subject* self, Observer* observer) {
    ConcreteSubject* subject = (ConcreteSubject*)self;
    for (int i = 0; i < subject->count; i++) {
        if (subject->observers[i] == observer) {
            for (int j = i; j < subject->count - 1; j++) {
                subject->observers[j] = subject->observers[j + 1];
            }
            subject->count--;
            break;
        }
    }
}

// 实现具体主题类的通知观察者方法
void ConcreteSubject_notifyObservers(Subject* self) {
    ConcreteSubject* subject = (ConcreteSubject*)self;
    for (int i = 0; i < subject->count; i++) {
        subject->observers[i]->update(subject->observers[i]);
    }
}

int main() {
    // 创建具体主题对象
    ConcreteSubject subject;
    memset(&subject, 0, sizeof(ConcreteSubject));
    subject.base.registerObserver = ConcreteSubject_registerObserver;
    subject.base.removeObserver = ConcreteSubject_removeObserver;
    subject.base.notifyObservers = ConcreteSubject_notifyObservers;

    // 创建具体观察者对象
    ConcreteObserver observer1, observer2;
    memset(&observer1, 0, sizeof(ConcreteObserver));
    memset(&observer2, 0, sizeof(ConcreteObserver));
    observer1.base.update = ConcreteObserver_update;
    observer2.base.update = ConcreteObserver_update;
    strcpy(observer1.name, "Observer 1");
    strcpy(observer2.name, "Observer 2");

    // 注册观察者
    subject.base.registerObserver(&subject.base, (Observer*)&observer1);
    subject.base.registerObserver(&subject.base, (Observer*)&observer2);

    // 改变主题对象的状态
    subject.state = 1;

    // 通知观察者
    subject.base.notifyObservers(&subject.base);

    // 删除观察者

总结

  • 使用面向对象的抽象, Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合。
  • 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。
  • 观察者自己决定是否需要订阅通知,目标对象对此一无所知。
  • 观察者模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。
目录
相关文章
|
15天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
|
1月前
|
设计模式 传感器
【设计模式】观察者模式(定义 | 特点 | Demo入门讲解)
【设计模式】观察者模式(定义 | 特点 | Demo入门讲解)
39 0
|
10天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
21 3
|
22天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
33 9
|
21天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
27 2
|
29天前
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性和扩展性
【10月更文挑战第13天】 本文将探讨PHP中常见的设计模式及其在实际项目中的应用。通过对比传统编程方式,我们将展示设计模式如何有效地提高代码的可维护性和扩展性。无论是单例模式确保类的单一实例,还是观察者模式实现对象间的松耦合,每一种设计模式都为开发者提供了解决特定问题的最佳实践。阅读本文后,读者将能更好地理解和应用这些设计模式,从而提升PHP编程的效率和质量。
|
30天前
|
设计模式 监控 UED
设计模式之观察者模式
【10月更文挑战第12天】 观察者模式是一种行为型设计模式,定义了一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都会自动更新。主要由主题(被观察者)和观察者组成,实现对象间的松耦合,广泛应用于用户界面、事件驱动系统和数据监控等领域。
|
1月前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
本教程详细讲解Kotlin语法,适合深入学习。对于快速掌握Kotlin,推荐“简洁”系列教程。本文特别介绍了观察者模式,包括使用Java API和Kotlin委托属性(如Delegates.observable)实现的方法,旨在帮助开发者更高效地实现和优化观察者模式的应用。
32 3
|
1月前
|
设计模式 SQL 安全
PHP中的设计模式:单例模式的深入探索与实践在PHP开发领域,设计模式是解决常见问题的高效方案集合。它们不是具体的代码,而是一种编码和设计经验的总结。单例模式作为设计模式中的一种,确保了一个类仅有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的基本概念、实现方式及其在PHP中的应用。
单例模式在PHP中的应用广泛,尤其在处理数据库连接、日志记录等场景时,能显著提高资源利用率和执行效率。本文从单例模式的定义出发,详细解释了其在PHP中的不同实现方法,并探讨了使用单例模式的优势与注意事项。通过对示例代码的分析,读者将能够理解如何在PHP项目中有效应用单例模式。
|
2月前
|
设计模式 算法 PHP
PHP中的设计模式:提升代码的灵活性与可维护性
在本文中,我们将深入探讨PHP编程语言中的一种重要概念——设计模式。设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它代表了最佳的实践,被有经验的面向对象的软件开发人员所采用。本文将通过具体的实例,展示如何在PHP项目中应用设计模式,以提高代码的灵活性和可维护性。无论你是PHP初学者还是经验丰富的开发者,都能从中获得有价值的见解。

热门文章

最新文章

  • 1
    C++一分钟之-设计模式:工厂模式与抽象工厂
    42
  • 2
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    46
  • 3
    C++一分钟之-C++中的设计模式:单例模式
    54
  • 4
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    38
  • 5
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    62
  • 6
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    57
  • 7
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    41
  • 8
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    50
  • 9
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    106
  • 10
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    78