基于C++的《Head First设计模式》笔记——抽象工厂模式

目录

一.专栏简介

二.依赖很强的代码

三.对象依赖

四.依赖倒置原则

五.应用原则

六.依赖倒置原则中,“倒置”在哪?

七.帮助我们遵循该原则的几条指南

八.原料工厂

九.重做披萨

十.我们做了什么

十一.定义抽象工厂

十二.比较工厂方法和抽象工厂


一.专栏简介

本专栏是我学习《head first》设计模式的笔记。这本书中是用Java语言为基础的,我将用C++语言重写一遍,并且详细讲述其中的设计模式,涉及是什么,为什么,怎么做,自己的心得等等。希望阅读者在读完我的这个专题后,也能在开发中灵活且正确的使用,或者在面对面试官时,能够自信地说自己熟悉常用设计模式。

在本专栏上一篇工厂模式的学习之后,本章将开始抽象工厂模式的学习。

二.依赖很强的代码

暂时忘掉上一篇的内容,假装我们从未听说过OO工厂。下面是一个“依赖性很强”、没有使用工厂的PizzaStore版本。代码如下:

class DependentPizzaStore { public: Pizza* createPizza(const string& style, const string& type) { Pizza* pizza = nullptr; if (style == "NY") { if (type == "cheese") pizza = new NYStyleCheesePizza(); else if (type == "greek") pizza = new NYStyleGreekPizza(); else if (type == "pepperoni") pizza = new NYStylePepperoniPizza(); else return nullptr; } else if (style == "Chicago") { if (type == "cheese") pizza = new ChicagoStyleCheesePizza(); else if (type == "greek") pizza = new ChicagoStyleGreekPizza(); else if (type == "pepperoni") pizza = new ChicagoStylePepperoniPizza(); else return nullptr; } else return nullptr; pizza->prepare(); pizza->bake(); pizza->cut(); pizza->box(); return pizza; } };

这个类依赖了8个具体类,如果添加一个加州风味的Pizza,将会依赖12个类。

三.对象依赖

当直接实例化一个对象时,我们在依赖于其具体类。上面“非常依赖”的PizzaStore。它就在PizzaStore类中创建所有披萨对象,而不是委托给工厂。

如果我们画图表达该PizzaStore版本以及它依赖的所有对象,看起来像这样:

四.依赖倒置原则

很显然,在我们的代码中减少对具体类的依赖是一件“好事”。事实上,有一个OO设计原则正式阐明了这一点;这个原则甚至还有一个响亮又正式的名称:依赖倒置原则(Dependency Inversion Principle)。

通用原则如下:

首先,这个原则听起来很像“针对接口编程,不针对实现编程”,对吧?是很像,但是,依赖倒置原则更强调抽象。该原则说明,高层组件不应该依赖于低层组件,而且,它们都应该依赖于抽象

“高层”组件是一个类,其行为以其他“低层”组件的形式定义。

例如,PizzaStore是一个高层组件,因为它的行为以披萨的形式定义:PizzaStore创建所有不同的披萨对象、准备、烘焙、切片、装盒;而所有的披萨是低层组件。

现在,这个原则告诉我们,应该重新编写我们的代码,以便依赖于抽象,而不是具体类。这对高层模块和低层模块都适用。

我们来考虑把这个原则应用到我们“非常依赖”的PizzaStore实现......

五.应用原则

现在,“非常依赖”版PizzaStore的主要问题是,它依赖于每个披萨类型,因为实际上它是在自己的orderPizza()方法中,实例化具体类型。

虽然已经创建了一个抽象,即Pizza,不过我们是在代码中创建具体的Pizza,因此,这个抽象没有带来太多好处。

怎样将这些实例化从orderPizza()方法拿出来?正如我们知道的,工厂方法模式正好允许我们做到这一点。

因此,在我们应用工厂方法模式之后,图形看起来如下:

应用工厂方法之后,你会注意到,高层组件PizzaStore以及低层组件(也就是这些披萨)都依赖于抽象,Pizza。工厂方法不是唯一遵循依赖倒置原则的技巧,但它是最有威力的一个。

六.依赖倒置原则中,“倒置”在哪?

依赖倒置原则名称中的“倒置”,是因为它倒转了通常考虑OO设计的方式。看看上面的图,低层组件现在依赖于更高层的抽象。同样,高层组件也绑定到同一抽象。因此,之前自上而下的依赖图自己倒转过来了,高层和低层模块现在都依赖于抽象

七.帮助我们遵循该原则的几条指南

  • 变量不应该持有到具体类的引用(如果使用new,就会持有到具体类的引用。通过使用工厂来绕开!)
  • 类不应该派生自具体类(如果派生自具体类,就会依赖具体类。派生自一个抽象类)
  • 方法不应该覆盖其任何基类的已实现方法(如果覆盖已实现的方法,那么基类就不是一个真正适合被继承的抽象。基类中这些已实现的方法,应该由所有子类共享)

但是,如果我们完全遵循这几条指南,我们连一个简单的程序都写不出来。任何程序都有违反这些指南的地方!!!

我们应该尽量遵循,而不是当成任何时候都应该遵循的铁律。

但是,如果我们把这些指南融会贯通,在设计时藏在大脑深处,当违反原则时,我们会知道在违反原则,并且会有一个好的理由。例如,如果我们知道有一个类不可能变化,那么,在我们的代码中实例化一个具体类也不是世界末日。想一想,我们一直不假思索地实例化std::string对象,违反这个原则了吗?有。可以这么做吗?可以!为什么,因为std::string不可能改变。

另一方面,如果一个类有可能变化,可以用一些良好的技巧,像工厂方法,来封装变化。

八.原料工厂

为了确保加盟店使用高质量的原料,我们打算建造一家生产原料的工厂,并将原料配送到各家加盟店!

对于这个计划,现在只有一个问题:加盟店坐落在不同的区域,纽约的红酱料和芝加哥的红酱料是不一样的。因此,你有一组需要配送到纽约的原料,另一组不同的原料配送到芝加哥。我们来看得更仔细一点:

现在,我们打算建造一个工厂来创建原料。该工厂将负责创建原料家族中的每一个原料。换句话说,工厂需要创建面团、酱、芝士等。

我们首先为工厂定义一个接口,该接口创建所有原料:

class PizzaIngredientFactory { public: virtual Dough* createDough() = 0; virtual Sauce* createSauce() = 0; virtual Cheese* createCheese() = 0; virtual vector<Veggies*> createVeggies() = 0; virtual Pepperoni* createPepperoni() = 0; virtual Clams* createClam() = 0; };

有了这个接口,我们打算这样做:

  1. 为每个区域建造一个工厂。你需要创建一个PizzaIngredientFactory的子类来实现每个创建方法。
  2. 实现一组要和工厂一起使用的原料类,像ReggianoCheese、RedPeppers和ThickCrustDough。这些类可以在合适的区域之间共享。
  3. 然后,我们依然需要把所有这些连接起来,把新的原料工厂整合进老的PizzaStore代码。

纽约和芝加哥原料工厂代码如下:

Ingredient.h:

class NYPizzaIngredientFactory : public PizzaIngredientFactory { public: Dough* createDough() override; Sauce* createSauce() override; Cheese* createCheese() override; vector<Veggies*> createVeggies() override; Pepperoni* createPepperoni() override; Clams* createClam() override; }; class ChicagoPizzaIngredientFactory : public PizzaIngredientFactory { public: Dough* createDough() override; Sauce* createSauce() override; Cheese* createCheese() override; vector<Veggies*> createVeggies() override; Pepperoni* createPepperoni() override; Clams* createClam() override; };

Ingredient.cpp:

#include "Ingredient.h" Dough* NYPizzaIngredientFactory::createDough() { return new ThinCrustDough(); } Sauce* NYPizzaIngredientFactory::createSauce() { return new MarinaraSauce(); } Cheese* NYPizzaIngredientFactory::createCheese() { return new ReggianoChess(); } vector<Veggies*> NYPizzaIngredientFactory::createVeggies() { return { new Garlic(), new Onion(), new Mushroom(), new RedPepper() }; } Pepperoni* NYPizzaIngredientFactory::createPepperoni() { return new SlicedPepperoni(); } Clams* NYPizzaIngredientFactory::createClam() { return new FreshClams(); } Dough* ChicagoPizzaIngredientFactory::createDough() { return new ThickCrustDough(); } Sauce* ChicagoPizzaIngredientFactory::createSauce() { return new PlumTomatoSauce(); } Cheese* ChicagoPizzaIngredientFactory::createCheese() { return new MozzarellaCheese(); } vector<Veggies*> ChicagoPizzaIngredientFactory::createVeggies() { return { new BalckOlives(), new Spinach(), new EggPlant() }; } Pepperoni* ChicagoPizzaIngredientFactory::createPepperoni() { return new SlicedPepperoni(); } Clams* ChicagoPizzaIngredientFactory::createClam() { return new FrozenClams(); }

九.重做披萨

工厂已经一切就绪,要准备生产高质量原料了。现在,我们只需要重做我们的披萨,让它只使用工厂生产的原料。先从抽象Pizza类开始:

Pizza.h:

class Pizza { public: virtual void prepare() = 0; virtual void bake(); virtual void cut(); virtual void box(); const string& getName(); protected: string name; Dough* dough; Sauce* sauce; vector<Veggies*> veggies; Cheese* cheese; Pepperoni* pepperoni; Clams* clam; };

将prepare()变成纯虚函数。

Pizza.cpp:

void Pizza::bake() { cout << "350°烘焙25分钟" << endl; } void Pizza::cut() { cout << "将披萨按照对角线切割" << endl; } void Pizza::box() { cout << "将披萨装盒" << endl; } const string& Pizza::getName() { return name; }

其他的方法保持不变,除了prerpare方法。

NYCheesPizza和ChicagoCheesePizza代码

Pizza.h:

class NYStyleCheesePizza : public Pizza { PizzaIngredientFactory* ingerdientFactory; public: NYStyleCheesePizza(PizzaIngredientFactory* factory); void prepare() override; }; class ChicagoStyleCheesePizza : public Pizza { PizzaIngredientFactory* ingerdientFactory; public: ChicagoStyleCheesePizza(PizzaIngredientFactory* factory); void prepare() override; void cut() override; };

Pizza.cpp:

NYStyleCheesePizza::NYStyleCheesePizza(PizzaIngredientFactory* factory) { ingerdientFactory = factory; } void NYStyleCheesePizza::prepare() { cout << "prepare " << name << endl; dough = ingerdientFactory->createDough(); sauce = ingerdientFactory->createSauce(); veggies = ingerdientFactory->createVeggies(); cheese = ingerdientFactory->createCheese(); pepperoni = ingerdientFactory->createPepperoni(); clam = ingerdientFactory->createClam(); } ChicagoStyleCheesePizza::ChicagoStyleCheesePizza(PizzaIngredientFactory* factory) { ingerdientFactory = factory; } void ChicagoStyleCheesePizza::prepare() { cout << "prepare " << name << endl; dough = ingerdientFactory->createDough(); sauce = ingerdientFactory->createSauce(); veggies = ingerdientFactory->createVeggies(); cheese = ingerdientFactory->createCheese(); pepperoni = ingerdientFactory->createPepperoni(); clam = ingerdientFactory->createClam(); } void ChicagoStyleCheesePizza::cut() { cout << "将披萨按照正方形线切割" << endl; }

prepare()方法一步一步地创建芝士披萨,每次需要原料时,就请工厂生产。

Pizza的代码用所组合的工厂生产披萨所用的原料。所生产的原料依赖所用的工厂。Pizza类不关心,它知道如何制作披萨。现在,Pizza从区域原料差异解耦,无论工厂是在奥斯汀、纳什维尔还是其他地方,Pizza类可以轻易地复用。

披萨店代码:

Pizza.h:

class PizzaStore { public: Pizza* orderPizza(string type); protected: virtual Pizza* createPizza(string type) = 0; }; class NYStylePizzaStore : public PizzaStore { private: Pizza* createPizza(string type) override; }; class ChicagoStylePizzaStore : public PizzaStore { private: Pizza* createPizza(string type) override; };

Pizza.cpp:

Pizza* PizzaStore::orderPizza(string type) { Pizza* pizza = createPizza(type); pizza->prepare(); pizza->bake(); pizza->cut(); pizza->box(); return pizza; } Pizza* NYStylePizzaStore::createPizza(string type) { Pizza* pizza = nullptr; PizzaIngredientFactory* ingredientFactory = new NYPizzaIngredientFactory(); if (type == "cheese") { //cout << "NYStyleCheesePizza" << endl; pizza = new NYStyleCheesePizza(ingredientFactory); pizza->setName("NYStyleCheesePizza"); } else if (type == "pepperoni") { cout << "NYStylePepperoniPizza" << endl; //pizza = new NYStylePepperoniPizza(); } else if (type == "greek") { cout << "NYStyleGreekPizza" << endl; //pizza = new NYStyleGreekPizza(); } return pizza; } Pizza* ChicagoStylePizzaStore::createPizza(string type) { Pizza* pizza = nullptr; PizzaIngredientFactory* ingredientFactory = new ChicagoPizzaIngredientFactory(); if (type == "cheese") { //cout << "ChicagoStyleCheesePizza" << endl; pizza = new ChicagoStyleCheesePizza(ingredientFactory); pizza->setName("ChicagoStyleCheesePizza"); } else if (type == "pepperoni") { cout << "ChicagoStylePepperoniPizza" << endl; //pizza = new ChicagoStylePepperoniPizza(); } else if (type == "greek") { cout << "ChicagoStyleGreekPizza" << endl; //pizza = new ChicagoStyleGreekPizza(); } return pizza; } NYStyleCheesePizza::NYStyleCheesePizza(PizzaIngredientFactory* factory) { ingerdientFactory = factory; } void NYStyleCheesePizza::prepare() { cout << "prepare " << name << endl; dough = ingerdientFactory->createDough(); sauce = ingerdientFactory->createSauce(); veggies = ingerdientFactory->createVeggies(); cheese = ingerdientFactory->createCheese(); pepperoni = ingerdientFactory->createPepperoni(); clam = ingerdientFactory->createClam(); } ChicagoStyleCheesePizza::ChicagoStyleCheesePizza(PizzaIngredientFactory* factory) { ingerdientFactory = factory; } void ChicagoStyleCheesePizza::prepare() { cout << "prepare " << name << endl; dough = ingerdientFactory->createDough(); sauce = ingerdientFactory->createSauce(); veggies = ingerdientFactory->createVeggies(); cheese = ingerdientFactory->createCheese(); pepperoni = ingerdientFactory->createPepperoni(); clam = ingerdientFactory->createClam(); } void ChicagoStyleCheesePizza::cut() { cout << "将披萨按照正方形线切割" << endl; }

运行结果:

我们在点一个披萨时,先准备了原料,这些原料都是从抽象工厂获得的,然后再烘焙,切割,装盒。

十.我们做了什么

一连串的代码变化,我们到底做了什么?通过引入一个称为抽象工厂的新工厂类型,我们提供了为披萨创建原料家族的方法。

抽象工厂给我们一个创建产品家族的接口。通过使用这个接口编写代码,我们把代码从实际创建产品的工厂解耦。这让我们在为不同上下文(例如不同区域、不同操作系统或者不同视感)生产产品时,能够实现工厂的变化。

因为代码从实际产品解耦,我们可以替换不同的工厂来获得不同的行为(例如获得意式番茄酱,而不是李子番茄酱)。

十一.定义抽象工厂

我们又给模式家族添加了另一个工厂模式,这个模式可以创建产品的家族。我们来看看这个模式的官方定义:

抽象工厂模式提供一个接口来创建相关或依赖对象的家族,而不需要指定具体类。

无疑,我们已经看到,抽象工厂允许客户使用一个抽象接口来创建一组相关的产品,而不需要懂得(或关心)实际生产的具体产品是什么。通过这样的方法,客户就从所有的特定产品解耦。我们来看看类图,了解其中的关系。

工厂方法其实潜伏在抽象工厂里面。抽象工厂的方法经常实现为工厂方法。抽象工厂的定义是定义一个接口,这个接口创建一组产品。这个接口的每个方法负责创建一个具体产品,我们实现抽象工厂的子类,以提供这些实现。因此,在抽象工厂中,用工厂方法来实现生产方法,是相当自然的方式。

十二.比较工厂方法和抽象工厂

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

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

相关文章

读懂 sap.m.URLHelper 文档里的 library: sap.m 与 Module: sap/m/library

你在 SAP UI5 API Reference 里看到的这两个字段&#xff0c;看起来都在讲 sap.m&#xff0c;但它们回答的是两类完全不同的问题&#xff1a; library: sap.m&#xff1a;这条 API 属于哪个 UI5 库&#xff08;library 维度&#xff1a;交付、依赖、主题、预加载等的组织单位&…

macOS恢复模式终端备份脚本:无依赖、保层级、避冲突的完整方案

macOS恢复模式终端备份脚本&#xff1a;无依赖、保层级、避冲突的完整方案 文章目录macOS恢复模式终端备份脚本&#xff1a;无依赖、保层级、避冲突的完整方案一、恢复模式备份的核心痛点二、核心功能与实现逻辑三、关键技术点与解决方案1. 替代缺失命令&#xff1a;用纯bash实…

【图像传输】基于matlab GUI格雷码编码MPSK图像传输系统【含Matlab源码 14896期】含报告

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到海神之光博客之家&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49…

【图像加密】基于matlab DNA结合arnold置乱变换实现彩色图像加密解密【含Matlab源码 14897期】

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到海神之光博客之家&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49…

【图像传输】格雷码编码MPSK图像传输系统【含GUI Matlab源码 14896期】含报告

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;Matlab武动乾坤博客之家&#x1f49e;…

TDengine Python 连接器入门指南

TDengine Python 连接器入门指南 本文面向 TDengine 初学者&#xff0c;目标是让你在 5&#xff5e;10 分钟内完成&#xff1a;安装连接器 → 建立连接 → 建库建表 → 写入 → 查询&#xff0c;并掌握连接器的基本使用方式与常见问题排查。 说明&#xff1a;TDengine 官方 Py…

基于Simulink的微电网中储能主控单元协调运行仿真

目录 手把手教你学Simulink 一、引言:为什么微电网需要“储能主控单元”? 二、系统整体架构 控制角色分配: 三、理论基础:下垂控制(Droop Control) 1. 有功-频率下垂(P-f) 2. 无功-电压下垂(Q-V) 四、Simulink 建模全流程 步骤1:主电路搭建(Simscape Elec…

Qt OpenGL 生成Mipmap技术详解

Qt OpenGL 生成Mipmap技术详解1. Mipmap技术概述1.1 Mipmap金字塔结构1.2 Mipmap的优势2. Qt中生成Mipmap的实现2.1 基本方法2.2 参数详解3. 高级应用技巧3.1 自定义Mipmap生成3.2 性能优化技巧4. 实际应用案例4.1 3D地形渲染4.2 2D游戏中的精灵渲染5. 常见问题解答5.1 Mipmap会…

【图像加密】DNA结合arnold置乱变换实现彩色图像加密解密【含Matlab源码 14897期】

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;Matlab武动乾坤博客之家&#x1f49e;…

【图像加密】混沌系统和DNA编码图像加密解密【含Matlab源码 14898期】含论文

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;Matlab武动乾坤博客之家&#x1f49e;…

情感分析在广告效果评估中的创新应用

情感分析在广告效果评估中的创新应用关键词&#xff1a;情感分析、广告效果评估、创新应用、自然语言处理、数据挖掘摘要&#xff1a;本文聚焦于情感分析在广告效果评估中的创新应用。首先介绍了情感分析和广告效果评估的基本概念&#xff0c;阐述了将二者结合的背景和意义。接…

导师不会告诉你:8款免费AI论文工具,轻松搞定真实参考文献!

警告&#xff1a; 这篇内容可能会颠覆你对“写论文”这件事的认知。你的同学和学长姐&#xff0c;可能正在用这些工具悄悄“开挂”。如果不想在Deadline前通宵达旦、反复被导师打回重改&#xff0c;请务必认真读完。 你是不是也有过这样的经历&#xff1f;面对空白的Word文档&a…

2026零基础小白能不能转行AI大模型?不会代码不懂编程?这一篇文章让你搞明白!

前几天一个硕士期间搞材料的朋友&#xff0c;考上了某大学的人工智能博士&#xff0c;有点焦虑&#xff0c;我们聊了聊。 他焦虑的重点主要集中在硕士期间一直在做材料相关的课题&#xff0c;编程基础薄弱&#xff0c;参与的人工智能课题少&#xff0c;担心读了博士之后&#…

痛定思痛:我花了一个月重构 AI 助手,只为了解决这三个问题

写在前面 说实话&#xff0c;第一次用官方 Claude Code 的时候&#xff0c;我是真兴奋。终端里跑着 AI&#xff0c;能帮我改代码、跑命令&#xff0c;感觉像有了个 24 小时在线的高级工程师搭档。 但用了两周后&#xff0c;我开始纠结。 为什么每次切换模型都要重启会话&…

异地就医备案-须知内容

1、适用人群 (1)异地长期居住人员,包括异地安置退休人员、异地长期居住人员、常驻异地工作人员等长期在异地居住、生活、工作的人员。 (2)临时外出就医人员,包括异地转诊就医人员,因工作、旅游等原因异地急诊抢救人员以及其他临时外出就医人员。 2、备案材料 (1)异…

基于SpringBoot的拼装模型销售管理系统的设计与实现(源码+lw+部署文档+讲解等)

课题介绍 随着拼装模型爱好者群体不断扩大&#xff0c;模型销售市场规模持续增长&#xff0c;但当前拼装模型销售行业普遍存在商品品类繁杂难管理、订单处理效率低下、库存管控不精准、客户需求响应滞后等问题&#xff0c;制约了商家运营效率与用户购物体验的提升。本课题以搭建…

SAP UI5 概念辨析:namespace,library 和 module 的理解

本文笔者从接到教程学习者的一个实际问题开始。 这位朋友想使用 URLHelper 的 redirect 功能,在 SAP UI5 应用里进行页面跳转。 他查询 SAP UI5 帮助文档,看到了这个 redirect 方法的输入参数说明: 然后看到这个 URLHelper 抬头区域的三个字段: 类型为 namespace libra…

基于SpringBoot的陪诊服务平台系统(源码+lw+部署文档+讲解等)

课题介绍随着人口老龄化加剧、医疗资源分布不均衡&#xff0c;独居老人、行动不便者、异地就医人群等普遍面临就医流程繁琐、无人陪同协助的困境&#xff0c;陪诊服务需求持续增长&#xff0c;但当前陪诊市场存在服务标准不统一、供需对接低效、服务过程缺乏监管、资金结算不规…

基于SpringBoot的农村客运服务系统(源码+lw+部署文档+讲解等)

课题介绍随着乡村振兴战略深入推进&#xff0c;农村地区出行需求持续增长&#xff0c;但当前农村客运普遍存在线路规划不合理、班次信息不透明、票务管理滞后、运营监管低效等问题&#xff0c;制约了客运服务质量提升&#xff0c;难以满足群众便捷出行需求。本课题以优化农村客…

关系系统架构升级指南:从“好友模式”到“心动模式”的平滑迁移

资深后端工程师李峰在连续三周为“相亲对象”调试代码、优化简历、提供职场解决方案后&#xff0c;收到了对方诚挚的感谢&#xff1a;“你真是我见过最靠谱的技术伙伴&#xff01;”——这个评价比线上系统的致命Bug更让他无从排查。一、系统诊断&#xff1a;为何你的情感进程总…