当前位置: 首页 > news >正文

类和对象实操之【日期类】

✨个人主页: Yohifo
🎉所属专栏: C++修行之路
🎊每篇一句: 图片来源

  • The pessimist complains about the wind; the optimist expects it to change; the realist adjusts the sails.

    • 悲观主义者抱怨风;乐观主义者期望它改变;现实主义者调整风帆。

    日期类


文章目录

  • 🗓️前言
  • 🗓️正文
    • 📆类的定义
      • 📅合法性检验
      • 📅判断闰年
      • 📅获取年份天数
      • 📅获取月份天数
    • 📆运算符重载
      • 📅判断等于
      • 📅判断小于
      • 📅复用至所有判断
      • 📅重载流插入、提取
    • 📆日期+=天数
      • 📅核心思想
      • 📅代码实现
    • 📆日期-日期
      • 📅核心思想
      • 📅代码实现
    • 📆自加、自减操作
      • 📅前置
      • 📅后置
    • 📆程序源码
  • 🗓️总结


🗓️前言

在学完类和对象相关知识后,需要一个程序来供我们练习、巩固知识点,日期类就是我们练习的首选程序,日期类实现简单且功能丰富,相信在完整地将日期类实现后,能对类和对象有更好的掌握及更深的理解

日历


🗓️正文

为了更符合工程标准,这里采用三个文件的方式实现程序

用于声明类和方法的 .h 头文件

Date.h

用于实现类和方法的 .cpp 源文件

Date.cpp

用于测试功能的 .cpp 源文件

test.cpp

工程框架

📆类的定义

先简单定义一下每个类中都有的默认成员函数

//当前位于文件 Date.h 中
#pragma once
#include<iostream>
using std::cout;	//采用部分展开的方式
using std::cin;//采用命名空间
namespace Yohifo
{class Date{public://构造函数,频繁使用且短小的代码直接在类的声明中实现,成为内联函数Date(int year = 2023, int month = 2, int day = 11):_year(year), _month(month), _day(day){}//拷贝构造函数Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}//赋值重载函数Date& operator=(const Date& d){if (this == &d)return *this;_year = d._year;_month = d._month;_day = d._day;return *this;}//析构函数~Date(){_year = 1970;_month = 2;_day = 11;}private:int _year;	//年、月、日int _month;int _day;};
}

📅合法性检验

首先编写第一个函数:合法性检验

检验标准

  • 年不能为0
  • 月在区间 [1, 12] 内,超过为非法
  • 根据年月推算出天数,天数不能操作规定天数,也不能 <= 0

注意:

  • 当前包括后续函数都是采取先在头文件 Date.h 的类中声明,再到 Date.cpp 实现的路径
  • 因历史原因导致的闰年变动这里不考虑,该程序实现的是理想情况下的闰年状态
  • 程序计算范围覆盖至公元前,限度为 [INT_MIN, INT_MAX]
#include"Date.h"using namespace Yohifo;	//全局展开命名空间//合法性检验
bool Date::check() const
{//年不能为0年if (_year == 0)return false;//月份区间 [1, 12]if (_month < 1 || _month > 12)return false;//天数要合理// getMonthDay 函数后续实现if (_day < 1 || _day > getMonthDay())return false;return true;
}

📅判断闰年

闰年二月多一天,因此需要特殊处理

闰年判断技巧: 四年一闰且百年不闰 或者 四百年一闰

//闰年判断
bool Date::checkLeapYear() const
{//按照技巧判断if (((_year % 4 == 0) && (_year % 100 != 0)) || (_year % 400 == 0))return true;elsereturn false;
}

📅获取年份天数

闰年多一天,为 366 ,非闰年为 365,判断返回即可

//获取年份天数
int Date::getYearDay() const
{//复用代码return (checkLeapYear() ? 366 : 365);
}

📅获取月份天数

根据当前年份和月份,判断当月有多少天

注意: 闰年的二月需要特殊处理

//获取月份天数
int Date::getMonthDay() const
{//非闰年情况下每个月天数,其中下标代表月份int arr[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };//如果为2月,且为闰年,直接返回 2月+1天if (_month == 2 && checkLeapYear())return arr[_month] + 1;elsereturn arr[_month];
}

📆运算符重载

前面学习了 operator 运算符重载,现在正好可以拿来练练手

📅判断等于

两个日期相等的前提是 都相等

//运算符重载
//判断等于
bool Date::operator==(const Date& d) const
{return ((_year == d._year) && (_month == d._month) && (_day == d._day));
}

📅判断小于

注意: 我们的运算顺序都是 左操作数右操作数,其中隐含的 this 指针默认为 左操作数

*this 小于 d 的逻辑

  • 首选判断年是否小于
  • 年相等,判断月是否小于
  • 年相等,月相等,判断天是否小于
//判断小于
bool Date::operator<(const Date& d) const
{if (_year < d._year)	//判断年return true;else if (_year == d._year && _month < d._month)	//判断月return true;else if (_year == d._year && _month == d._month && _day < d._day)	//判断天return true;elsereturn false;
}

📅复用至所有判断

善用代码复用,有了等于和小于,我们可以直接写出所有判断

//判断不等于
bool Date::operator!=(const Date& d) const
{return !(*this == d);	//等于,取反为不等于
}//判断小于等于
bool Date::operator<=(const Date& d) const
{//小于、等于成立一个即可return ((*this < d) || (*this == d));
}//判断大于
bool Date::operator>(const Date& d) const
{//即不小于,也不等于return (!(*this < d) && !(*this == d));
}//判断大于等于
bool Date::operator>=(const Date& d) const
{//大于或等于return ((*this > d) || (*this == d));
}

📅重载流插入、提取

coutcin 只能输出、输出内置类型,但如果我们对它进行改造一下,就能直接输出我们的自定义类型

注意:

  • cout 类型为 ostreamcin 类型为 istream
  • 要使得 coutcin 变为重载后的左操作数,此时的运算符重载就不能写在类内,因为在类中的函数默认 this 为第一个参数,即左操作数
  • 因此这两个函数比较特殊,需要写在外面,但同时又得访问类中的成员,此时就需要 友元函数
  • 两个函数都有返回值,返回的就是coutcin本身,避免出现 cout << d1 << d2 这种情况

此时可以利用合法性检验了

实现 operator>> 时,右操作数不能用 const 修饰

//在 Date.h 内
//新增几个局部展开
using std::ostream;
using std::istream;
using std::endl;namespace Yohifo
{class Date{//声明为类的友元函数friend std::ostream& operator<<(std::ostream& out, const Date& d2);friend std::istream& operator>>(std::istream& in, Date& d2);	//注意//……};//直接定义在头文件中,成为内联函数inline ostream& operator<<(ostream& out, const Date& d){//此时需要检验日期合法性if (Date(d).check() == false){out << "警告,当前日期非法!" << endl;out << "后续操作将会受到限制" << endl;}out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;}inline istream& operator>>(istream& in, Date& d){Date tmp;flag:cout << "请入日期,格式为:年 月 日" << endl;in >> tmp._year >> tmp._month >> tmp._day;//如果输入日期非法,就重新输入if (Date(tmp).check() == false){cout << "警告,当前日期非法!" << endl;cout << "日期输入失败,请尝试重新输入!" << endl;goto flag;}cout << "输入成功!" << endl;return in;}
}

有了这两个运算符重载后,我们就可以直接对自定义类型(日期类对象)直接进行输入输出操作了

Date d1;
cin >> d1;	//对自定义类型的输入
cout << d1;	//对自定义类型的输出

📆日期+=天数

下面涉及两个重要算法

  • 日期 += 天数
  • 日期 -= 天数

这里把 日期 += 天数 介绍清楚了,日期 -= 天数 就很好写了,就是倒着走

  • 有了 日期 += 天数 后,可以直接实现 日期 + 天数
  • 同理也可以实现 日期 - 天数

📅核心思想

注:此时实现的是 日期+=天数

进位思想:天数满了后进位到月份上,月份满后进位至年份上

进位

注意:

  • 每个月对应天数都需要计算,因为每个月都不同
  • 月份为12月时,再+就变成了下一年的一月
  • 假设为公元前,加至0年时,需要特殊处理为公元1年
  • += 操作返回的是左操作数本身,应对 (d1 += 10) = 20 这种情况

📅代码实现

//日期+=天数
Date& Date::operator+=(const int val)
{if (check() == false){cout << "警告,当前日期非法,无法进行操作" << endl;return *this;}//判断 val,避免恶意操作,如 d1 += -100if (val < 0){//此时需要调用 -=*this -= (-val);return *this;}//因为是 += 不需要创建临时对象//首先把天数全部加至 _day 上_day += val;//获取当前月份天数int monthDay = getMonthDay();//判断进位,直至 _day <= monthDaywhile (_day > monthDay){//此时大于,先把多余的天数减掉_day -= monthDay;//此时进位一个月++_month;//判断月份是否大于 12if (_month > 12){//此时需要进年++_year;//月份变为1月_month = 1;//判断是否为0年if (_year == 0)_year = 1;	//调整}//重新获取月份天数monthDay = getMonthDay();}//返回 *this 本身return *this;
}

有了这个函数后,我们就可以根据当前日期推算 N 天后的日期
运行结果

日期+天数 可以直接复用上面的代码,而 日期-=天数 将逻辑反过来就行了,这里不展示代码了,完整代码在文末的 gitee 仓库中


📆日期-日期

日期+日期无意义,但日期-日期有,可以计算两日期差值

日期相减有两种情况:

  • 左操作数小于右操作数,此时返回大于0的值
  • 左操作数大于右操作数,此时返回小于0的值

具体实现时也很好处理,直接用一个 flag 就行了

📅核心思想

先不管左右操作数大小,我们先找出较大操作数与较小操作数

通过较小操作数逐渐逼近较大操作数,其中经过的天数就是差值

步骤:

  • 先把日期对齐,即小操作数日期与大操作数日期平齐
  • 再把月份对齐
  • 最后再把年份对齐就行了
  • 随着步骤的深入,天数计算会越来越快的

除了这种方法外,我们还可以直接一天一天的加,直到相等,当然这种效率较低

📅代码实现

//日期 - 日期
const int Date::operator-(const Date& d) const
{if (check() == false || d.check() == false){cout << "警告,当前日期非法,无法进行操作!默认返回 0" << endl;return 0;}//假设右操作数为较大值Date max(d);Date min(*this);int flag = 1;//判断if (min > max){max = *this;min = d;flag = -1;}//小的向大的靠近int daySum = 0;//考虑天while (min._day != max._day){min += 1;daySum++;}//考虑月while (min._month != max._month){daySum += min.getMonthDay();min += min.getMonthDay();}//考虑年while (min._year != max._year){daySum += min.getYearDay();min._year++;}return daySum * flag;
}

这种方法(同轴转动)将会带来一定的性能提升(相对逐天相加来说)

方法相差 1k 年相差 1w 年相差 10w 年
同轴转动耗时 0 ms耗时 0 ms耗时 2 ms
逐天相加耗时 28 ms耗时 297 ms耗时 3142 ms

注:实际差异与电脑性能有关


📆自加、自减操作

自加操作实现很简单,不过需要注意编译器是如何区分两者的

占位参数

  • 因为前置与后置的运算符重载函数名一致,此时需要给运算符多加一个参数以区分,这是由编译器规定的合法行为,占位参数加在后置运算符重载中

📅前置

前置直接复用前面 += 的代码

前置操作是先进行自加或自减,再返回

//前置++
Date& Date::operator++()
{//直接复用*this += 1;return *this;
}//前置--
Date& Date::operator--()
{*this -= 1;return *this;
}

📅后置

此时需要借助 占位参数,当启用时,编译器会自动传参,并自动区分,占位参数 类型为 int

后置操作是先记录值,再进行自加或自减,返回之前记录的值

//后置++
const Date Date::operator++(int)
{//借助临时变量Date tmp(*this);*this += 1;return tmp;
}//后置--
const Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}

特别注意: 对于自定义类型来说,在进行自加、自减操作时,最好采用前置,因为后置会发生拷贝构造行为,造成资源浪费


📆程序源码

完整的代码在这里 Gitee
动图


🗓️总结

以上就是关于日期类实现的全部内容了,涉及到了前面学的大部分知识,希望大家在看完后能把它独立敲一遍,加深理解

如果你觉得本文写的还不错的话,可以留下一个小小的赞👍,你的支持是我分享的最大动力!

如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正


星辰大海

相关文章推荐
类和对象合集系列
类和对象(下)
类和对象(上)
类和对象(中)

===============

C++入门必备
C++入门基础

感谢支持

相关文章:

类和对象实操之【日期类】

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f38a;每篇一句&#xff1a; 图片来源 The pessimist complains about the wind; the optimist expects it to change; the realist adjusts the sails. 悲观主义者抱怨风;乐观主义者期望它…...

微搭中如何实现弹性布局

我们在实际开发中经常可能会有一些社交的场景&#xff0c;比如开发一个类似朋友圈九宫格图片展示的功能。因为图片的数量不确定&#xff0c;所以需要实现图片的从左到右顺序排列。 在微搭中可以以可视化的方式设置样式。但是对于我们这类特殊需求&#xff0c;只用可视化设置显…...

九龙证券|外资强势出手!这只科创板百元股,被疯狂加仓

本周&#xff0c;北上资金净买入29.32亿元&#xff0c;连续第13周加仓A股。分商场看&#xff0c;北上资金加仓重点倾向于沪市的白马蓝筹股&#xff0c;沪股通取得50.34亿元&#xff0c;深股通则被净卖出21.02亿元。 食品饮料本周取得逾23亿元的增持&#xff0c;居职业首位&…...

51单片机最强模块化封装(4)

文章目录 前言一、创建key文件,添加key文件路径二、key文件编写三、模块化测试总结前言 本篇文章将为大家带来按键的模块化封装,这里使用到了三行按键使得我们的代码更加简便。 按键原理:独立按键 一、创建key文件,添加key文件路径 这里的操作就不过多解释了,大家自行看…...

五、Git本地仓库基本操作——分支管理

1. 什么是分支&#xff1f; master分支 我们在初始化git仓库的时候&#xff0c;会默认创建一个master分支&#xff0c;HEAD指针这时就会默认执行master分支。当我们在master分支提交&#xff08;commit&#xff09;了更新之后&#xff0c;master分支就会指向当前当前最新的co…...

vscode搭建python Django网站开发环境

这里使用pip安装的方式&#xff0c;打开命令行&#xff0c;输入执行&#xff1a; pip install django2.2这里选择安装2.2版本是因为是新的lts版本&#xff0c;长期支持稳定版。 接下来再安装pillow&#xff0c;Django底层一部分是基于pillow进行的。 pip install pillowpylint…...

【mybatis】实现分页查询

一 .使用原生分页器的实体类 1.1 java代码部分 方法多 不易书写 package cn.bdqn.entity;public class Page {private Integer pageIndex;//页码private Integer pageSize;//页大小 显示多少行数据private Integer totalCounts;//数据的总行数private Integer totalPages;//…...

CF1560D Make a Power of Two 题解

CF1560D Make a Power of Two 题解题目链接字面描述题面翻译题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示思路代码实现备注题目 链接 https://www.luogu.com.cn/problem/CF1560D 字面描述 题面翻译 给定一个整数 nnn。每次操作你可以做两件事情中的一件&am…...

C#开发的OpenRA的读取文件的函数

C#开发的OpenRA的读取文件的函数 在OpenRA游戏里,读取文件是必备的功能。 因为游戏大部分文件都是图片、动画、语音。 很久以前,我以为开发游戏的主要功能是在程序开发上, 其实游戏的大部分工作都不是在开发上,而是在美工方面。 因为游戏跟电影是一样,就是不断地展示场景,…...

SpringBoot结合XXL-JOB实现定时任务

Quartz的不足 Quartz 的不足&#xff1a;Quartz 作为开源任务调度中的佼佼者&#xff0c;是任务调度的首选。但是在集群环境中&#xff0c;Quartz采用API的方式对任务进行管理&#xff0c;这样存在以下问题&#xff1a; 通过调用API的方式操作任务&#xff0c;不人性化。需要…...

【Node.js】 创建web服务器

Node.js什么是客户端&#xff0c;什么是服务器服务器和普通电脑的区别什么是http模块导入http模块服务器相关概念创建web服务器的基本步骤req请求对象req响应对象解决中文乱码根据不同的url响应不同的html内容什么是客户端&#xff0c;什么是服务器 客户端在网络节点中&#x…...

基于go语言实现RestFul交互

一、RestFul 1.1 RestFul的介绍 RESTFUL&#xff08;Representational State Transfer&#xff09;是一种网络应用程序的设计风格和开发方式&#xff0c;基于HTTP或HTTPS&#xff0c;可以使用XML格式定义或JSON格式定义。RESTFUL适用于移动互联网厂商作为业务接口的场景&…...

情感溢出:读《浣溪沙》

浣溪沙 谁念西风独自凉 作者 纳兰性德 谁念西风独自凉&#xff0c;萧萧黄叶闭疏窗&#xff0c;沉思往事立残阳。 被酒莫惊春睡重&#xff0c;赌书消得泼茶香&#xff0c;当时只道是寻常。 记得年轻时学这篇词&#xff0c;就是愣背&#xff0c;现在也就记得这句当时只道是寻常…...

深入解读.NET MAUI音乐播放器项目(一):概述与架构

系列文章将分步解读音乐播放器核心业务及代码&#xff1a; 深入解读.NET MAUI音乐播放器项目&#xff08;一&#xff09;&#xff1a;概述与架构深入解读.NET MAUI音乐播放器项目&#xff08;二&#xff09;&#xff1a;播放内核深入解读.NET MAUI音乐播放器项目&#xff08;三…...

【Python小游戏】某程序员将套圈游戏玩儿到了巅峰,好嗨哟~Pygame代码版《牛牛套圈》已上线,大人的套圈游戏太嗨了,小孩勿进。

前言 世上选择那么多。 关注栗子同学会是您最明智的选择哦。 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众hao即可免费。 “幸运牛牛套圈圈”套住欢乐&#xff0c;圈住幸福&#xff0c;等你来挑战&#xf…...

php的declare命令如何使用?

php中的declare结构用来设定一段代码的执行指令declare用于执行3个指令&#xff1a;ticks,encoding,strict_typesdeclare结构用于全局范围&#xff0c;影响到其后的所有代码&#xff08;但如果有declare结构的文件被其他文件包含&#xff0c;则对包含他的父文件不起作用&#x…...

嵌软工程师要掌握的硬件知识2:一文看懂什么开漏和推挽电路(open-drain / push-pull)

想了解开漏和推挽,就要先了解一下三极管和场效应管是什么,在其他章节有详细介绍,本文就不再进行赘述。 1 推挽(push pull)电路 1.1 理解什么是推挽电路 - 详细介绍 如图所示,Q3是个NPN型三极管,Q4是个PNP型三极管。 1)当Vin电压为正时,上面的N型三极管控制端有电…...

1.2.6存储结构-磁盘管理:单缓冲区与双缓冲区读取、流水线周期、计算流水线执行时间

1.2.6存储结构-磁盘管理&#xff1a;单缓冲区与双缓冲区读取、流水线周期、计算流水线执行时间流水线周期计算流水线执行时间微秒&#xff0c;时间单位&#xff0c;符号μs&#xff08;英语&#xff1a;microsecond &#xff09;&#xff0c;1微秒等于百万分之一秒&#xff08;…...

【pytest接口自动化测试】结合单元测试框架pytest+数据驱动模型+allure

api&#xff1a; 存储测试接口 conftest.py :设置前置操作 目前前置操作&#xff1a;1、获取token并传入headers&#xff0c;2、获取命令行参数给到环境变量,指定运行环境commmon&#xff1a;存储封装的公共方法 connect_mysql.py&#xff1a;连接数据库http_requests.py: 封装…...

展锐平台WIFI吞吐问题解决方案

同学,别退出呀,我可是全网最牛逼的 WIFI/BT/GPS/NFC分析博主,我写了上百篇文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。 一、Wi-Fi 吞吐验收标准 预置条件:屏蔽房;DUT 距离 AP 1m 左右;测试 AP 不加密;…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...