零基础设计模式——创建型模式 - 抽象工厂模式

news2025/5/24 13:09:57

第二部分:创建型模式 - 抽象工厂模式 (Abstract Factory Pattern)

我们已经学习了单例模式(保证唯一实例)和工厂方法模式(延迟创建到子类)。现在,我们来探讨创建型模式中更为复杂和强大的一个——抽象工厂模式。它处理的是创建“产品族”的问题。

  • 核心思想:提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。

抽象工厂模式 (Abstract Factory Pattern)

“提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。”

想象一下,你要装修房子,可以选择不同的装修风格,比如“现代风格”、“中式风格”或“欧式风格”。

  • 每种风格都包含一系列配套的家具:沙发、茶几、电视柜等。
  • “现代风格”的家具(现代沙发、现代茶几)是一套,它们之间风格统一。
  • “中式风格”的家具(红木沙发、雕花茶几)是另一套,它们也风格统一。

你不会用一个现代风格的沙发去搭配一个中式雕花的茶几,这样会显得不伦不类。抽象工厂模式就是用来确保你得到的是一整套风格协调的产品。

1. 目的 (Intent)

抽象工厂模式的主要目的:

  1. 创建产品族:核心在于创建一系列相关的或相互依赖的对象(称为一个“产品族”)。例如,一个UI工具包工厂可能创建按钮、文本框、滚动条等一系列UI组件,这些组件需要有统一的外观和行为(如Windows风格或macOS风格)。
  2. 客户端与具体类解耦:客户端代码只与抽象的工厂接口和抽象的产品接口打交道,而不需要知道具体是哪个工厂的实现,也不需要知道具体的产品类名。
  3. 保证产品兼容性:由同一个具体工厂创建出来的产品,一定是相互兼容、可以协同工作的。

2. 生活中的例子 (Real-world Analogy)

  • 电脑组装

    • 抽象工厂 (AbstractFactory)ComputerPartsFactory (定义了创建CPU、主板、内存等组件的接口)。
    • 具体工厂 (ConcreteFactory)IntelCompatibleFactory (生产Intel CPU、兼容主板、特定内存条),AMDCompatibleFactory (生产AMD CPU、兼容主板、另一特定内存条)。
    • 抽象产品 (AbstractProduct)CPU, Motherboard, RAM (这些是组件的抽象接口)。
    • 具体产品 (ConcreteProduct)IntelCPU, AMDRyzenCPU, AsusMotherboard, GigabyteMotherboard, KingstonRAM, CorsairRAM
      当你选择 IntelCompatibleFactory 时,你会得到一套相互兼容的Intel平台组件。你不会得到一个Intel的CPU却配一个只支持AMD的主板。
  • 换肤功能 (Skinnable UI)

    • 抽象工厂UIThemeFactory (定义 createButton(), createCheckbox(), createWindow() 等方法)。
    • 具体工厂WindowsThemeFactory (创建Windows风格的按钮、复选框、窗口),MacThemeFactory (创建Mac风格的按钮、复选框、窗口),DarkThemeFactory (创建暗黑主题的组件)。
    • 抽象产品Button, Checkbox, Window (UI组件的接口)。
    • 具体产品WindowsButton, MacButton, DarkButton 等。
      用户选择一个主题(比如“暗黑主题”),应用就会使用 DarkThemeFactory 来创建所有UI元素,确保界面风格统一。

3. 结构 (Structure)

抽象工厂模式通常包含以下角色:

  1. AbstractFactory (抽象工厂):声明一个创建抽象产品对象的操作接口集合。通常每个抽象产品对应一个创建方法。
  2. ConcreteFactory (具体工厂):实现 AbstractFactory 接口,负责创建具体产品族中的产品对象。系统可以有多个具体工厂,每个具体工厂创建一个具体的产品族。
  3. AbstractProduct (抽象产品):为一类产品对象声明一个接口。系统中可以有多个不同的抽象产品,构成产品族。
  4. ConcreteProduct (具体产品):定义一个将被相应的具体工厂创建的产品对象。它实现了 AbstractProduct 接口。
  5. Client (客户端):仅使用 AbstractFactory 和 AbstractProduct 接口。客户端不关心具体是哪个工厂、哪个产品,它只知道它需要一个工厂来创建它需要的产品。
    在这里插入图片描述

4. 适用场景 (When to Use)

  • 一个系统要独立于它的产品的创建、组合和表示时。即,你希望客户端代码与具体产品的创建过程分离。
  • 一个系统要由多个产品系列中的一个来配置时。例如,系统需要支持多种“外观感觉”(Look and Feel)。
  • 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
  • 当你提供一个产品类库,而只想显示它们的接口而不是实现时。

简单来说:

  • 需要创建的产品对象有复杂的关联关系(属于同一个产品族)
  • 系统需要支持不同系列(族)的产品,并且可以在运行时切换

5. 优缺点 (Pros and Cons)

优点:

  1. 分离接口和实现:客户端使用抽象接口,与具体的产品实现解耦。
  2. 易于交换产品系列:改变具体工厂即可改变整个产品系列,客户端代码无需修改。
  3. 有利于产品的一致性:当一个系列的产品对象被设计成一起工作时,抽象工厂模式能够保证客户端始终只使用同一个产品系列中的对象。

缺点:

  1. 难以扩展新的产品种类 (Product Kind):如果要在产品族中增加一个新的产品种类(例如,在UI主题工厂中增加创建 ScrollBar 的方法),那么所有的抽象工厂接口和具体工厂实现都需要修改,这违反了开闭原则。对于这种情况,工厂方法模式可能更合适(每个产品种类一个工厂方法)。
  2. 类的数量会显著增加:每增加一个产品族,就需要增加一套对应的具体产品类和具体工厂类。

6. 实现方式 (Implementations)

让我们通过一个跨平台UI组件的例子来看看抽象工厂模式的实现。假设我们需要为Windows和macOS创建风格一致的按钮和文本框。

抽象产品 (Button, TextBox)
// ui_elements.go
package ui

// Button 按钮接口 (抽象产品A)
type Button interface {
	Render()
	OnClick()
}

// TextBox 文本框接口 (抽象产品B)
type TextBox interface {
	Render()
	SetText(text string)
	GetText() string
}
// Button.java
package com.example.ui;

// 按钮接口 (抽象产品A)
public interface Button {
    void render();
    void onClick();
}

// TextBox.java
package com.example.ui;

// 文本框接口 (抽象产品B)
public interface TextBox {
    void render();
    void setText(String text);
    String getText();
}
具体产品 (WindowsButton, WindowsTextBox, MacButton, MacTextBox)
// windows_elements.go
package ui

import "fmt"

// WindowsButton Windows风格按钮 (具体产品A1)
type WindowsButton struct{}

func (b *WindowsButton) Render()  { fmt.Println("Rendering a Windows style button.") }
func (b *WindowsButton) OnClick() { fmt.Println("Windows button clicked.") }

// WindowsTextBox Windows风格文本框 (具体产品B1)
type WindowsTextBox struct{ text string }

func (tb *WindowsTextBox) Render()        { fmt.Println("Rendering a Windows style text box.") }
func (tb *WindowsTextBox) SetText(text string) { tb.text = text }
func (tb *WindowsTextBox) GetText() string   { return tb.text }

// mac_elements.go
package ui

import "fmt"

// MacButton Mac风格按钮 (具体产品A2)
type MacButton struct{}

func (b *MacButton) Render()  { fmt.Println("Rendering a macOS style button.") }
func (b *MacButton) OnClick() { fmt.Println("macOS button clicked.") }

// MacTextBox Mac风格文本框 (具体产品B2)
type MacTextBox struct{ text string }

func (tb *MacTextBox) Render()        { fmt.Println("Rendering a macOS style text box.") }
func (tb *MacTextBox) SetText(text string) { tb.text = text }
func (tb *MacTextBox) GetText() string   { return tb.text }
// WindowsButton.java
package com.example.ui.windows;

import com.example.ui.Button;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

// Windows风格按钮 (具体产品A1)
public class WindowsButton implements Button {
    @Override
    public void render() {
        System.out.println("Rendering a Windows style button.");
        // 实际场景中可能会使用Swing/JavaFX等创建真实UI
        // JFrame frame = new JFrame("Windows Button");
        // JButton button = new JButton("Win Button");
        // button.addActionListener(e -> onClick());
        // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // frame.setLayout(new FlowLayout());
        // frame.add(button);
        // frame.setSize(200, 100);
        // frame.setVisible(true);
    }

    @Override
    public void onClick() {
        System.out.println("Windows button clicked.");
        // JOptionPane.showMessageDialog(null, "Windows Button Clicked!");
    }
}

// WindowsTextBox.java
package com.example.ui.windows;

import com.example.ui.TextBox;

// Windows风格文本框 (具体产品B1)
public class WindowsTextBox implements TextBox {
    private String text = "";
    @Override
    public void render() {
        System.out.println("Rendering a Windows style text box: [" + text + "]");
    }
    @Override
    public void setText(String text) { this.text = text; }
    @Override
    public String getText() { return this.text; }
}

// MacButton.java
package com.example.ui.mac;

import com.example.ui.Button;

// Mac风格按钮 (具体产品A2)
public class MacButton implements Button {
    @Override
    public void render() {
        System.out.println("Rendering a macOS style button.");
    }
    @Override
    public void onClick() {
        System.out.println("macOS button clicked.");
    }
}

// MacTextBox.java
package com.example.ui.mac;

import com.example.ui.TextBox;

// Mac风格文本框 (具体产品B2)
public class MacTextBox implements TextBox {
    private String text = "";
    @Override
    public void render() {
        System.out.println("Rendering a macOS style text box: (" + text + ")");
    }
    @Override
    public void setText(String text) { this.text = text; }
    @Override
    public String getText() { return this.text; }
}
抽象工厂 (GUIFactory)
// gui_factory.go
package ui

// GUIFactory 抽象UI工厂 (抽象工厂)
type GUIFactory interface {
	CreateButton() Button
	CreateTextBox() TextBox
}
// GUIFactory.java
package com.example.ui;

// 抽象UI工厂 (抽象工厂)
public interface GUIFactory {
    Button createButton();
    TextBox createTextBox();
}
具体工厂 (WindowsFactory, MacFactory)
// windows_factory.go
package ui

// WindowsFactory Windows UI工厂 (具体工厂1)
type WindowsFactory struct{}

func (wf *WindowsFactory) CreateButton() Button {
	return &WindowsButton{}
}
func (wf *WindowsFactory) CreateTextBox() TextBox {
	return &WindowsTextBox{}
}

// mac_factory.go
package ui

// MacFactory Mac UI工厂 (具体工厂2)
type MacFactory struct{}

func (mf *MacFactory) CreateButton() Button {
	return &MacButton{}
}
func (mf *MacFactory) CreateTextBox() TextBox {
	return &MacTextBox{}
}
// WindowsFactory.java
package com.example.ui.windows;

import com.example.ui.Button;
import com.example.ui.GUIFactory;
import com.example.ui.TextBox;

// Windows UI工厂 (具体工厂1)
public class WindowsFactory implements GUIFactory {
    @Override
    public Button createButton() {
        System.out.println("WindowsFactory: Creating WindowsButton");
        return new WindowsButton();
    }
    @Override
    public TextBox createTextBox() {
        System.out.println("WindowsFactory: Creating WindowsTextBox");
        return new WindowsTextBox();
    }
}

// MacFactory.java
package com.example.ui.mac;

import com.example.ui.Button;
import com.example.ui.GUIFactory;
import com.example.ui.TextBox;

// Mac UI工厂 (具体工厂2)
public class MacFactory implements GUIFactory {
    @Override
    public Button createButton() {
        System.out.println("MacFactory: Creating MacButton");
        return new MacButton();
    }
    @Override
    public TextBox createTextBox() {
        System.out.println("MacFactory: Creating MacTextBox");
        return new MacTextBox();
    }
}
客户端使用 (Application)
// main.go (示例用法)
/*
package main

import (
	"fmt"
	"./ui" // 假设 ui 包在当前目录下
	"runtime"
)

// Application 客户端,它不知道具体的工厂和产品类
type Application struct {
	factory GUIFactory
	button  Button
	textBox TextBox
}

func NewApplication(factory ui.GUIFactory) *Application {
	app := &Application{factory: factory}
	app.button = factory.CreateButton()
	app.textBox = factory.CreateTextBox()
	return app
}

func (app *Application) Run() {
	app.button.Render()
	app.button.OnClick()
	app.textBox.SetText("Hello Abstract Factory!")
	app.textBox.Render()
	fmt.Println("Text from box:", app.textBox.GetText())
}

func main() {
	var factory ui.GUIFactory
	// 根据操作系统选择不同的工厂
	os := runtime.GOOS
	fmt.Println("Operating System:", os)

	if os == "windows" {
		factory = &ui.WindowsFactory{}
	} else if os == "darwin" { // darwin is macOS
		factory = &ui.MacFactory{}
	} else {
		fmt.Println("Unsupported OS, defaulting to Windows style.")
		factory = &ui.WindowsFactory{} // 默认或提供一个通用工厂
	}

	app := NewApplication(factory)
	app.Run()
}
*/
// Application.java (客户端)
package com.example;

import com.example.ui.Button;
import com.example.ui.GUIFactory;
import com.example.ui.TextBox;
import com.example.ui.mac.MacFactory;
import com.example.ui.windows.WindowsFactory;

public class Application {
    private Button button;
    private TextBox textBox;

    public Application(GUIFactory factory) {
        System.out.println("Client: Configuring application with a UI factory.");
        button = factory.createButton();
        textBox = factory.createTextBox();
    }

    public void run() {
        System.out.println("\nClient: Running the application UI...");
        button.render();
        button.onClick();
        textBox.setText("Hello Abstract Factory!");
        textBox.render();
        System.out.println("Text from box: " + textBox.getText() + "\n");
    }

    // Main.java (示例用法)
    /*
    public static void main(String[] args) {
        GUIFactory factory;
        Application app;

        String osName = System.getProperty("os.name").toLowerCase();
        System.out.println("Operating System: " + osName);

        if (osName.contains("win")) {
            factory = new WindowsFactory();
        } else if (osName.contains("mac")) {
            factory = new MacFactory();
        } else {
            System.out.println("Unsupported OS, defaulting to Windows style.");
            factory = new WindowsFactory(); // Default factory
        }

        app = new Application(factory);
        app.run();

        // 假设我们现在想切换到Mac主题 (如果当前不是Mac)
        if (!osName.contains("mac")) {
            System.out.println("\n--- Switching to Mac Theme for demonstration ---");
            factory = new MacFactory();
            app = new Application(factory);
            app.run();
        }
    }
    */
}

7. 与工厂方法模式的区别

抽象工厂模式和工厂方法模式是初学者容易混淆的两个模式。

  • 工厂方法模式 (Factory Method)

    • 关注点:创建单个产品对象
    • 结构:一个抽象工厂接口(通常只有一个创建方法 factoryMethod()),多个具体工厂实现它来创建不同的具体产品。
    • 目的:延迟产品的实例化到子类。
    • 解决问题:如何创建一个对象,但让子类决定具体创建哪个对象。
  • 抽象工厂模式 (Abstract Factory)

    • 关注点:创建一系列相关的产品对象(一个产品族)
    • 结构:一个抽象工厂接口(包含多个创建不同种类产品的抽象方法,如 createProductA(), createProductB()),多个具体工厂实现它来创建属于同一个产品族的不同具体产品。
    • 目的:提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。
    • 解决问题:如何创建一组相互关联/依赖的对象,并保证它们之间是兼容的。

简单来说

  • 如果你只需要创建一种产品,但希望由子类决定具体创建哪种类型,用工厂方法
  • 如果你需要创建多种产品,这些产品需要配套使用(属于一个系列/族),并且希望客户端与具体产品解耦,用抽象工厂

实际上,抽象工厂模式的实现中,每个具体工厂内部的创建方法(如 createButton())通常可以使用工厂方法模式来实现,或者直接 new 具体产品。

8. 总结

抽象工厂模式是创建型模式中功能最强大但也相对复杂的模式之一。它通过提供一个抽象接口来创建一系列相关的产品对象(产品族),使得客户端代码可以独立于具体的产品实现。这对于需要支持多种产品系列(例如不同的UI主题、不同的数据库实现)并且希望在它们之间轻松切换的系统非常有用。

记住它的核心:创建产品家族,保证兼容性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2384613.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解锁内心的冲突:神经症冲突的理解与解决之道

目录 一、神经症冲突概述 二、冲突的基本类型 三、未解决冲突的后果 四、尝试解决的途径 五、真正解决冲突 六、总结 干货分享,感谢您的阅读! 人类的内心世界复杂多变,常常充满了各种冲突和矛盾。每个人在成长的过程中,都或…

Redisson读写锁和分布式锁的项目实践

解决方案:采用读写锁 什么是读写锁 Redisson读写锁是一种基于Redis实现特殊的机制,用于在分布式系统中协调对共享资源的访问,其继承了Java中的ReentrantReadWriteLock的思想.特别适用于读多写少的场景.其核心是:允许多个线程同时读取共享资源,但写操作必须占用资源.从而保证线…

SkyWalking高频采集泄漏线程导致CPU满载排查思路

SkyWalking高频采集泄漏线程导致CPU满载排查思路 契机 最近在消除线上服务告警,发现Java线上测试服经常CPU满载告警,以前都是重启解决,今天好好研究下,打arthas火焰图发现是SkyWalking-agent的线程采集任务一直在吃cpu&#xff…

【HarmonyOS 5】Map Kit 地图服务之应用内地图加载

#HarmonyOS SDK应用服务,#Map Kit,#应用内地图 目录 前期准备 AGC 平台创建项目并创建APP ID 生成调试证书 生成应用证书 p12 与签名文件 csr 获取 cer 数字证书文件 获取 p7b 证书文件 配置项目签名 配置签名证书指纹 项目开发 配置Client I…

ld: cpu type/subtype in slice (arm64e.old) does not match fat header (arm64e)

ld: cpu type/subtype in slice (arm64e.old) does not match fat header (arm64e) in ‘/Users/*****/MposApp/MposApp/Modules/Common/Mpos/NewLand/MESDK.framework/MESDK’ clang: error: linker command failed with exit code 1 (use -v to see invocation) 报错 解决方…

通过vue-pdf和print-js实现PDF和图片在线预览

npm install vue-pdf npm install print-js <template><div><!-- PDF 预览模态框 --><a-modal:visible"showDialog":footer"null"cancel"handleCancel":width"800":maskClosable"true":keyboard"…

视频监控管理平台EasyCVR结合AI分析技术构建高空抛物智能监控系统,筑牢社区安全防护网

高空抛物严重威胁居民生命安全与公共秩序&#xff0c;传统监管手段存在追责难、威慑弱等问题。本方案基于EasyCVR视频监控与AI视频分析技术&#xff08;智能分析网关&#xff09;&#xff0c;构建高空抛物智能监控系统&#xff0c;实现24小时实时监测、智能识别与精准预警&…

2.2.1 05年T1复习

引言 从现在进去考研英语基础阶段的进阶&#xff0c;主要任务还是05-09年阅读真题的解题&#xff0c;在本阶段需要注意正确率。阅读最后目标&#xff1a;32-34分&#xff0c;也就是每年真题最多错四个。 做题步骤&#xff1a; 1. 预习&#xff1a;读题干并找关键词 做题&#…

Python-11(集合)

与字典类似&#xff0c;集合最大的特点就是唯一性。集合中所有的元素都应该是独一无二的&#xff0c;并且也是无序的。 创建集合 使用花括号 set {"python","Java"} print(type(set)) 使用集合推导式 set {s for s in "python"} print(set…

Opixs: Fluxim推出的全新显示仿真模拟软件

Opixs 是 Fluxim 最新研发的显示仿真模拟软件&#xff0c;旨在应对当今显示技术日益复杂的挑战。通过 Opixs&#xff0c;研究人员和工程师可以在制造前&#xff0c;设计并验证 新的像素架构&#xff0c;从而找出更功节能、色彩表现更优的布局方案。 Opixs 适用于学术研究和工业…

佰力博与您探讨PVDF薄膜极化特性及其影响因素

PVDF&#xff08;聚偏氟乙烯&#xff09;薄膜的极化是其压电性能形成的关键步骤&#xff0c;通过极化处理可以显著提高其压电系数和储能能力。极化过程涉及多种方法和条件&#xff0c;以下从不同角度详细说明PVDF薄膜的极化特性及其影响因素。 1、极化方法 热极化&#xff1a;…

自动获取ip地址安全吗?如何自动获取ip地址

在数字化网络环境中&#xff0c;IP地址的获取方式直接影响设备连接的便捷性与安全性。自动获取IP地址&#xff08;通过DHCP协议&#xff09;虽简化了配置流程&#xff0c;但其安全性常引发用户疑虑。那么&#xff0c;自动获取IP地址安全吗&#xff1f;如何自动获取IP地址&#…

STM32:深度解析RS-485总线与SP3485芯片

32个设备 知识点1【RS-485的简介】 RS-485是一种物理层差分总线标准&#xff0c;在串口的基础上演变而来&#xff1b; 两者虽然不在同一层次上直接对等&#xff0c;但在实际系统中&#xff0c;往往使用RS-485驱动差分总线&#xff0c;将USART转换为适合长距离、多点通信的物…

亚马逊搜索代理: 终极指南

文章目录 前言一、为什么需要代理来搜索亚马逊二、如何选择正确的代理三、搜索亚马逊的最佳代理类型四、为亚马逊搜索设置代理五、常见挑战及克服方法六、亚马逊搜索的替代方法总结 前言 在没有代理的情况下搜索亚马逊会导致 IP 禁止、验证码和速度限制&#xff0c;从而使数据…

C++笔记-封装红黑树实现set和map

1.源码及框架分析 上面就是在stl库中set和map的部分源代码。 通过上图对框架的分析&#xff0c;我们可以看到源码中rb_tree⽤了⼀个巧妙的泛型思想实现&#xff0c;rb_tree是实 现key的搜索场景&#xff0c;还是key/value的搜索场景不是直接写死的&#xff0c;⽽是由第⼆个模板…

留给王小川的时间不多了

王小川&#xff0c;这位头顶“天才少年”光环的清华学霸、搜狗输入法创始人、中国互联网初代技术偶像&#xff0c;正迎来人生中最难啃的硬骨头。 他在2023年创立的百川智能&#xff0c;被称为“大模型六小虎”之一。今年4月&#xff0c;王小川在全员信中罕见地反思过去两年工作…

国产频谱仪性能如何?矢量信号分析仪到底怎么样?

矢量信号分析仪是一种高性能的电子测量设备&#xff0c;具备频谱分析、矢量信号分析、实时频谱分析、脉冲信号分析、噪声系数测量、相位噪声测量等多种功能。它能够对各类复杂信号进行精确的频谱特性分析、调制质量评估、信号完整性检测以及干扰源定位等操作。广泛应用于通信、…

熔断器(Hystrix,Resilience4j)

熔断器 核心原理​ 熔断器通过监控服务调用失败率&#xff0c;在达到阈值时自动切断请求&#xff0c;进入熔断状态&#xff08;类似电路保险丝&#xff09;。其核心流程为&#xff1a; 关闭状态&#xff08;Closed&#xff09;​​&#xff1a;正常处理请求&#xff0c;统计失…

C++23 容器从其他兼容范围的可构造性与可赋值性 (P1206R7)

文章目录 背景与动机提案内容与实现细节提案 P1206R7实现细节编译器支持 对开发者的影响提高灵活性简化代码向后兼容性 总结 C23标准引入了对容器构造和赋值的新特性&#xff0c;这些特性使得容器能够更灵活地从其他兼容范围初始化&#xff0c;并支持从范围赋值。这些改进由提案…

多通道振弦式数据采集仪MCU安装指南

设备介绍 数据采集仪 MCU集传统数据采集器与5G/4G,LoRa/RS485两种通信功能与一体的智能数据采集仪。该产品提供振弦、RS-485等的物理接口&#xff0c;能自动采集并存储多种自然资源、建筑、桥梁、城市管廊、大坝、隧道、水利、气象传感器的实时数据&#xff0c;利用现场采集的数…