【再谈设计模式】观察者模式~对象间依赖关系的信使

一、引言

        在软件工程、软件开发的世界里,设计模式如同建筑蓝图中的经典结构,帮助开发者构建更加灵活、可维护和可扩展的软件系统。观察者模式就是其中一种极为重要的行为型设计模式,它在处理对象间的一对多关系时展现出独特的魅力。

二、定义与描述

        观察者模式定义了对象之间的一种一对多依赖关系。其中有一个被观察的对象(称为主题Subject)和多个观察该对象的观察者(Observer)。主题对象负责维护一组观察者对象,并在自身状态发生改变时通知所有观察者。这种模式使得对象之间的耦合度降低,主题和观察者可以独立地进行扩展和修改。

三、抽象背景

        在很多实际的软件场景中,存在着对象状态变化需要通知其他对象的需求。例如,在一个新闻发布系统中,当有新的新闻发布(新闻对象状态改变)时,订阅了该新闻频道的用户(观察者)需要及时得到通知;或者在一个游戏开发中,当游戏角色的某些属性(如生命值、位置等)发生变化时,与之相关的UI界面元素(观察者)需要更新显示。

四、适用场景与现实问题解决

  • 事件驱动系统
    • 在图形用户界面(GUI)开发中,用户的操作(如点击按钮、输入文本等)会触发事件。这些事件可以看作是主题的状态变化,而处理这些事件的各个组件(如菜单更新、数据显示等)就是观察者。通过观察者模式,可以很方便地实现事件的分发和处理,使不同的组件能够独立地响应事件。

  • 股票市场监测
    • 当股票价格发生变化(主题状态改变)时,多个投资者(观察者)需要得到通知以便做出相应的决策。使用观察者模式可以高效地实现这种通知机制,而不需要在股票价格变化的代码中硬编码每个投资者的通知逻辑。

观察者模式的现实生活的例子">五、观察者模式的现实生活的例子

  • 社交媒体平台
    • 当一个用户(主题)发布了一条新的动态时,他的好友(观察者)会收到通知。这里,用户是被观察的对象,好友们是观察者。社交平台负责维护好友关系(即主题中的观察者列表),并在用户发布新动态时通知所有好友。

  • 气象站与订阅者
    • 气象站(主题)负责收集气象数据并检测天气状态的变化。当天气状态发生变化(如温度、湿度、气压等数据变化)时,气象站会通知所有订阅了气象信息的用户(观察者),如农民、飞行员、户外运动爱好者等。

六、初衷与问题解决

  • 初衷
    • 观察者模式的初衷是为了实现对象之间的松耦合关系。在没有这种模式的情况下,如果一个对象的状态变化需要通知其他对象,可能会导致高度耦合的代码,即变化的对象需要直接调用其他对象的方法来通知它们。这使得代码难以维护和扩展,因为任何一个相关对象的改变都可能影响到其他对象。
  • 问题解决
    • 通过观察者模式,主题和观察者之间通过抽象的接口进行交互。主题只需要维护一个观察者列表,并在状态变化时调用观察者的抽象通知方法。这样,主题不需要知道具体的观察者类型,观察者也不需要知道主题的具体实现细节。当有新的观察者或主题需要加入系统时,只需要实现相应的接口即可,不会影响到其他部分的代码。

七、代码示例

Java示例

import java.util.ArrayList;
import java.util.List;

// 观察者接口
interface Observer {
    void update(String message);
}

// 主题类
class Subject {
    private List<Observer> observers = new ArrayList<>();
    private String state;

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void detach(Observer observer) {
        observers.remove(observer);
    }

    public void setState(String state) {
        this.state = state;
        notifyAllObservers();
    }

    private void notifyAllObservers() {
        for (Observer observer : observers) {
            observer.update(state);
        }
    }
}

// 具体观察者类
class ConcreteObserver implements Observer {
    private String name;

    public ConcreteObserver(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name + " received message: " + message);
    }
}

public class Main {
    public static void main(String[] args) {
        Subject subject = new Subject();

        Observer observer1 = new ConcreteObserver("Observer 1");
        Observer observer2 = new ConcreteObserver("Observer 2");

        subject.attach(observer1);
        subject.attach(observer2);

        subject.setState("New state!");
    }
}

类图:

  Subject类与Observer接口之间是一对多的关系(Subject可以有多个ObserverConcreteObserver类实现了Observer接口。 

C++示例

#include <iostream>
#include <vector>

// 观察者抽象类
class Observer {
public:
    virtual void update(std::string message) = 0;
};

// 主题类
class Subject {
private:
    std::vector<Observer*> observers;
    std::string state;
public:
    void attach(Observer* observer) {
        observers.push_back(observer);
    }

    void detach(Observer* observer) {
        for (auto it = observers.begin(); it!= observers.end(); ++it) {
            if (*it == observer) {
                observers.erase(it);
                break;
            }
        }
    }

    void setState(std::string state) {
        this.state = state;
        notifyAllObservers();
    }

    void notifyAllObservers() {
        for (auto observer : observers) {
            observer->update(state);
        }
    }
};

// 具体观察者类
class ConcreteObserver : public Observer {
private:
    std::string name;
public:
    ConcreteObserver(std::string name) : name(name) {}

    void update(std::string message) override {
        std::cout << name << " received message: " << message << std::endl;
    }
};

int main() {
    Subject subject;

    Observer* observer1 = new ConcreteObserver("Observer 1");
    Observer* observer2 = new ConcreteObserver("Observer 2");

    subject.attach(observer1);
    subject.attach(observer2);

    subject.setState("New state!");

    return 0;
}

Python示例

# 观察者抽象类
class Observer:
    def update(self, message):
        pass

# 主题类
class Subject:
    def __init__(self):
        self.observers = []
        self.state = None

    def attach(self, observer):
        self.observers.append(observer)

    def detach(self, observer):
        self.observers.remove(observer)

    def setState(self, state):
        self.state = state
        self.notifyAllObservers()

    def notifyAllObservers(self):
        for observer in self.observers:
            observer.update(self.state)


# 具体观察者类
class ConcreteObserver(Observer):
    def __init__(self, name):
        self.name = name

    def update(self, message):
        print(f"{self.name} received message: {message}")


if __name__ == "__main__":
    subject = Subject()

    observer1 = ConcreteObserver("Observer 1")
    observer2 = ConcreteObserver("Observer 2")

    subject.attach(observer1)
    subject.attach(observer2)

    subject.setState("New state!")

Go示例

package main

import (
    "fmt"
)

// 观察者接口
type Observer interface {
    update(message string)
}

// 主题结构体
type Subject struct {
    observers []Observer
    state     string
}

// 附加观察者
func (s *Subject) attach(observer Observer) {
    s.observers = append(s.observers, observer)
}

// 分离观察者
func (s *Subject) detach(observer Observer) {
    for i, obs := range s.observers {
        if obs == observer {
            s.observers = append(s.observers[:i], s.observers[i+1:]...)
            break
        }
    }
}

// 设置状态并通知观察者
func (s *Subject) setState(state string) {
    s.state = state
    s.notifyAllObservers()
}

// 通知所有观察者
func (s *Subject) notifyAllObservers() {
    for _, observer := range s.observers {
        observer.update(s.state)
    }
}

// 具体观察者结构体
type ConcreteObserver struct {
    name string
}

// 具体观察者实现更新方法
func (co *ConcreteObserver) update(message string) {
    fmt.Printf("%s received message: %s\n", co.name, message)
}

func main() {
    subject := Subject{}

    observer1 := ConcreteObserver{name: "Observer 1"}
    observer2 := ConcreteObserver{name: "Observer 2"}

    subject.attach(&observer1)
    subject.attach(&observer2)

    subject.setState("New state!")
}

观察者模式的优缺点">八、观察者模式的优缺点

  • 优点
    • 松耦合:主题和观察者之间是松耦合的关系。主题不需要知道观察者的具体实现,只需要调用观察者的抽象接口。这使得在系统中添加或删除观察者非常容易,不会影响到主题的代码。
    • 可扩展性:可以很容易地增加新的观察者,只需要实现观察者接口即可。同样,主题也可以在不影响观察者的情况下进行扩展。
    • 支持广播通信:一个主题可以通知多个观察者,实现了一对多的消息传递,适合于需要将信息广播给多个对象的场景。
  • 缺点
    • 可能存在通知顺序问题:如果有多个观察者,当主题通知观察者时,可能会存在通知顺序不确定的问题。这在某些对顺序有严格要求的场景下可能会导致问题。
    • 性能开销:如果观察者数量较多,当主题状态发生变化时,通知所有观察者可能会带来一定的性能开销。特别是在观察者的更新操作比较复杂时,这种开销会更加明显。

观察者模式的升级版" style="background-color:transparent;">九、观察者模式的升级版

  • 事件委托模型
    • 在传统的观察者模式中,主题直接通知所有的观察者。而在事件委托模型中,引入了事件源、事件和事件处理程序的概念。事件源(类似于主题)产生事件,事件包含了关于状态变化的信息,事件处理程序(类似于观察者)负责处理事件。事件委托模型更加灵活,可以根据事件的类型、优先级等因素来决定如何处理事件,而不是简单地通知所有观察者。
  • 反应式编程中的观察者模式扩展
    • 在反应式编程(如RxJava、ReactiveX等)中,观察者模式得到了进一步的扩展。反应式编程关注的是数据的流动和异步处理。在这种模式下,观察者可以对数据的变化做出反应,并且可以组合、转换和过滤数据。例如,在RxJava中,可以使用操作符来对数据流进行操作,然后再将处理后的结果通知给观察者。这使得观察者模式在处理异步和复杂的数据处理场景时更加高效和灵活。


http://www.niftyadmin.cn/n/5820408.html

相关文章

Spring Boot 3.0响应式编程:订阅与发布、Flow的使用场景及优势解析

一、背景知识 随着微服务架构的普及和对高并发、低延迟系统需求的增长&#xff0c;响应式编程已成为现代应用开发的主流趋势之一。传统的同步阻塞式I/O模型在处理大量并发请求时&#xff0c;会因为线程阻塞而导致性能下降。而响应式编程通过异步非阻塞的方式处理数据流&#x…

ofa.js:无需打包的MVVM框架,前端开发的轻量之选

近年来&#xff0c;前端开发领域涌现了许多优秀的框架&#xff0c;如React、Vue和Angular&#xff0c;它们极大地提升了开发效率和代码可维护性。然而&#xff0c;随着项目复杂度的增加&#xff0c;这些框架的学习曲线和构建工具链的复杂性也让许多开发者感到头疼。在这样的背景…

sqlserver统计一张表字段数量,sqlserver列出表所有字段

统计表中字段的个数 select a.name ,count(0) 字段总数 from sys.objects a inner join sys.all_columns b on a.object_idb.object_id where a.type‘U’ and a.name‘表名’ group by a.name 列出所有的表字段 1.每个字段一行 SELECT c.name AS ColumnName FROM sys.column…

操作系统之文件系统的基本概念

目录 用户和磁盘视角的文件 文件控制块&#xff08;FCB&#xff09;和索引结点&#xff08;inode&#xff09; 文件的操作 创建文件&#xff08;create系统调用&#xff09; 写文件&#xff08;write系统调用&#xff09; 读文件&#xff08;read系统调用&#xff09; 重…

Android 修改SVG属性并显示图片(AndroidSvg)

引入依赖&#xff1a; dependencies {implementation com.caverock:androidsvg-aar:1.4 }核心代码&#xff1a; import com.caverock.androidsvg.SVG import org.w3c.dom.Document import java.io.StringWriter import javax.xml.transform.OutputKeys import javax.xml.tran…

PyCharm 的安装与使用(Window)

1 PyCharm 简介 PyCharm 是一款由 JetBrains 公司开发的专门用于 Python 语言开发的集成开发环境&#xff08;IDE&#xff09;。以下是其相关介绍&#xff1a; 1.1 特点与功能 智能代码编辑&#xff1a;提供高度智能化的代码编辑器&#xff0c;支持语法高亮、自动补全、代码重…

基于springboot的甘肃非物质文化网站设计与实现源码(java+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的甘肃非物质文化网站设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 甘肃非物质…

idea下java的maven项目编译内存溢出GC overhead limit exceeded解决办法

在编译阶段出现“GC overhead limit exceeded”错误&#xff0c;通常与编译器本身的内存使用有关&#xff0c;而不是项目的运行时问题。这种情况常见于大型项目或复杂的编译设置。以下是一些具体的调整建议&#xff0c;帮助你在编译阶段解决内存问题&#xff1a; 1. 增加Java编…