【设计模式】SOLID设计原则
1、什么是SOLID设计原则
SOLID 是面向对象设计中的五个基本设计原则的首字母缩写,它们是:
单一职责原则(Single Responsibility Principle,SRP):
类应该只有一个单一的职责,即一个类应该有且只有一个改变的理由。这意味着一个类应该只负责一个特定的功能或任务,而不是多个不相关的功能。这样做可以提高类的内聚性,并使得类更容易理解、修改和测试。
开放-封闭原则(Open/Closed Principle,OCP):
软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着在不修改现有代码的情况下,应该能够通过添加新的代码来扩展系统的功能。这样做可以使得系统更加稳定,减少修改现有代码可能带来的风险。
里氏替换原则(Liskov Substitution Principle,LSP):
子类型必须能够替换其基类型。换句话说,任何可以接受基类型的地方都可以接受子类型,而且不会引发意外的行为。这样做可以保持系统的一致性和可靠性,并且确保使用继承时不会破坏代码的正确性。
接口隔离原则(Interface Segregation Principle,ISP):
客户端不应该被迫依赖于其不使用的接口。这意味着应该将接口设计成小而专注的接口,而不是大而臃肿的接口。这样做可以降低耦合性,并且使得系统更加灵活和易于维护。
依赖倒置原则(Dependency Inversion Principle,DIP):
高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。这样做可以降低模块之间的耦合度,并且使得系统更易于扩展和修改。
这些原则是由罗伯特·马丁(Robert C. Martin)等人在面向对象设计中提出的,它们提供了一套指导原则,帮助设计出高质量、可维护和可扩展的面向对象系统。
2、单一职责原则
单一职责原则(Single Responsibility Principle,SRP)要求一个类或模块应该只有一个单一的责任,即一个类或模块应该只负责一个特定的功能或任务。这样做可以提高代码的内聚性、可维护性和可测试性。
让我们通过一个简单的例子来说明单一职责原则:
假设我们有一个简单的应用程序,用于处理用户信息,包括保存用户信息到数据库和从数据库中检索用户信息。我们可以将这个功能拆分成两个类:一个负责保存用户信息,一个负责检索用户信息。
#include <iostream>
#include <string>// 负责保存用户信息到数据库
class UserSaver {
public:void saveUser(const std::string& username, const std::string& email) {// 将用户信息保存到数据库std::cout << "用户信息已保存到数据库:" << username << ", " << email << std::endl;}
};// 负责从数据库中检索用户信息
class UserRetriever {
public:void retrieveUser(const std::string& username) {// 从数据库中检索用户信息std::cout << "从数据库中检索到用户信息:" << username << std::endl;}
};int main() {UserSaver userSaver;userSaver.saveUser("Alice", "alice@example.com");UserRetriever userRetriever;userRetriever.retrieveUser("Alice");return 0;
}
在这个例子中,我们有两个类 UserSaver
和 UserRetriever
,它们分别负责保存用户信息和检索用户信息。这两个类各自都只有一个单一的职责,即负责一个特定的功能。如果我们需要修改保存用户信息的逻辑,我们只需要修改 UserSaver
类;如果我们需要修改检索用户信息的逻辑,我们只需要修改 UserRetriever
类。这样做提高了代码的可维护性,并且使得每个类更加简单和易于理解。
3、开放-封闭原则
开放-封闭原则(Open/Closed Principle,OCP)是面向对象设计中的一个基本原则,由柏拉图·梅特克斯(Bertrand Meyer)在他的《面向对象软件构造》(Object-Oriented Software Construction)一书中首次提出。它的核心思想是软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。换句话说,软件实体在不修改现有代码的情况下,应该能够通过添加新的代码来扩展系统的功能。
开放-封闭原则的目的是为了提高系统的可维护性、可扩展性和稳定性。通过遵循这一原则,可以使得系统更容易理解和修改,并且减少对现有代码的影响。
在实际应用中,可以通过以下几种方式来遵循开放-封闭原则:
抽象化:通过使用抽象类、接口或者抽象函数来定义可扩展的接口,从而使得系统可以根据需要进行扩展,而不必修改现有代码。
多态性:利用多态性和继承机制,使得系统可以通过添加新的子类来扩展功能,而不必修改基类或现有代码。
组合/聚合:通过组合或聚合关系来构建对象之间的关联关系,从而使得系统可以通过添加新的组件来扩展功能,而不必修改现有组件。
模块化:将系统分解成独立的模块或组件,使得每个模块只负责一个特定的功能,从而使得系统可以通过添加新的模块来扩展功能,而不必修改现有模块。
总之,开放-封闭原则指导我们设计出易于扩展和维护的软件系统,通过封装变化和利用多态性,使得系统可以根据需要进行扩展,而不必修改现有代码。
让我们通过一个简单的例子来说明开放-封闭原则。
假设我们有一个简单的图形绘制程序,它可以绘制不同形状的图形,包括圆形和矩形。现在我们希望在程序中添加新的图形类型,比如三角形。我们可以通过遵循开放-封闭原则来扩展程序的功能,而不必修改现有的代码。
首先,我们定义一个抽象基类 Shape
,它有一个纯虚函数 draw
用于绘制图形:
#include <iostream>// 抽象基类:图形
class Shape {
public:virtual void draw() const = 0;
};
然后,我们定义具体的图形类,比如 Circle
和 Rectangle
类,它们分别继承自 Shape
类并实现 draw
函数:
// 圆形类
class Circle : public Shape {
public:void draw() const override {std::cout << "绘制圆形\n";}
};// 矩形类
class Rectangle : public Shape {
public:void draw() const override {std::cout << "绘制矩形\n";}
};
现在,如果我们想要添加新的图形类型,比如三角形,我们只需要添加一个新的类 Triangle
,它也继承自 Shape
类并实现 draw
函数:
// 三角形类
class Triangle : public Shape {
public:void draw() const override {std::cout << "绘制三角形\n";}
};
通过这种方式,我们可以在不修改现有代码的情况下,通过添加新的类来扩展程序的功能,符合开放-封闭原则。这样做提高了代码的可维护性和可扩展性,使得系统更易于理解和修改。
4、 里氏替换原则
里氏替换原则(Liskov Substitution Principle,LSP)是面向对象设计中的一个基本原则,由芭芭拉·利斯科夫(Barbara Liskov)在 1987 年提出。该原则指出,子类型必须能够替换其基类型,即任何可以接受基类型的地方都可以接受子类型,而且不会引发意外的行为。
在更通俗的说法中,如果一个类型是子类型(派生类),那么它应该可以替换掉基类型(基类)并且不会破坏程序的正确性。换句话说,子类型应该保持基类型的行为,而不是产生意外的行为。
遵循里氏替换原则的目的是为了确保代码的一致性和可靠性,使得系统更易于理解、扩展和维护。如果违反了里氏替换原则,那么可能会导致程序的错误行为和不稳定性。
在实际应用中,可以通过以下几点来遵循里氏替换原则:
子类型必须实现基类型的所有行为,不能减少基类型的约束条件。
子类型可以增加新的行为,但不能修改基类型已有的行为。
子类型的前置条件(即输入条件)必须比基类型更宽松。
子类型的后置条件(即输出条件)必须比基类型更严格。
通过遵循里氏替换原则,可以确保系统的稳定性和可靠性,并且使得系统更易于扩展和维护。
让我们通过一个简单的例子来说明里氏替换原则。
假设我们有一个简单的几何图形类层次结构,包括基类 Shape
和两个子类 Rectangle
和 Square
,其中 Square
是 Rectangle
的子类。
现在让我们来看看是否满足里氏替换原则:
#include <iostream>// 基类:图形
class Shape {
public:virtual void draw() const {std::cout << "绘制图形\n";}
};// 矩形类
class Rectangle : public Shape {
public:void draw() const override {std::cout << "绘制矩形\n";}
};// 正方形类
class Square : public Rectangle {
public:void draw() const override {std::cout << "绘制正方形\n";}
};// 绘制图形函数
void drawShape(const Shape& shape) {shape.draw();
}int main() {Rectangle rectangle;Square square;drawShape(rectangle); // 绘制矩形drawShape(square); // 绘制正方形return 0;
}
在这个例子中,我们有一个基类 Shape
,它有一个 draw
方法用于绘制图形。然后,我们有一个 Rectangle
类和一个 Square
类,它们分别继承自 Shape
类,并且都重写了 draw
方法以实现各自特定的绘制行为。
在 main
函数中,我们创建了一个 Rectangle
对象和一个 Square
对象,并且分别调用了 drawShape
函数来绘制这些图形。
在这个例子中,Square
类是 Rectangle
类的子类,符合继承关系。而且,在 drawShape
函数中,我们可以接受 Shape
类型的参数,并且传入 Rectangle
或 Square
对象进行绘制,而不会产生意外的行为。
因此,这个例子满足了里氏替换原则:子类型(Square
)可以替换其基类型(Rectangle
)而不会引发意外的行为,程序的行为保持一致。
5、接口隔离原则
接口隔离原则(Interface Segregation Principle,ISP)是面向对象设计中的一个基本原则,由罗伯特·马丁(Robert C. Martin)在他的《敏捷软件开发:原则、模式和实践》(Agile Software Development, Principles, Patterns, and Practices)一书中提出。接口隔离原则指出,客户端不应该被迫依赖于其不使用的接口。换句话说,一个类不应该依赖于它不需要使用的接口,应该将接口设计成小而专注的接口,而不是大而臃肿的接口。
接口隔离原则的目的是为了提高系统的灵活性和可维护性。通过将接口拆分成小而专注的接口,可以降低类之间的耦合度,使得系统更易于理解、扩展和修改。同时,这也可以避免因为接口的臃肿而导致的功能耦合和代码冗余。
在实践中,可以通过以下几点来遵循接口隔离原则:
- 将大而臃肿的接口拆分成多个小而专注的接口,每个接口只包含一个单一的功能或职责。
- 只在需要使用某个接口的地方引入该接口,避免将不需要的接口强加给客户端。
- 根据客户端的需求,设计出合适的接口,并且保持接口的稳定性,避免频繁地修改接口。
通过遵循接口隔离原则,可以使得系统更灵活、更易于维护,并且能够更好地应对需求变化。
让我们通过一个简单的例子来说明接口隔离原则。
假设我们有一个简单的文件操作接口 FileOperation
,它定义了一些文件操作的方法,比如打开文件、读取文件和关闭文件等。然后,我们有两个类 TextEditor
和 ImageEditor
,它们分别实现了这个接口。
首先,让我们定义文件操作接口 FileOperation
:
#include <iostream>// 文件操作接口
class FileOperation {
public:virtual void open() = 0;virtual void read() = 0;virtual void close() = 0;
};
然后,我们有一个文本编辑器 TextEditor
,它需要实现文件操作接口来打开和读取文本文件:
// 文本编辑器类
class TextEditor : public FileOperation {
public:void open() override {std::cout << "打开文本文件\n";}void read() override {std::cout << "读取文本文件\n";}void close() override {std::cout << "关闭文本文件\n";}
};
接着,我们有一个图像编辑器 ImageEditor
,它也需要实现文件操作接口来打开和读取图像文件:
// 图像编辑器类
class ImageEditor : public FileOperation {
public:void open() override {std::cout << "打开图像文件\n";}void read() override {std::cout << "读取图像文件\n";}void close() override {std::cout << "关闭图像文件\n";}
};
在这个例子中,TextEditor
和 ImageEditor
都实现了 FileOperation
接口,但是它们只使用了其中的一部分方法(即打开和读取文件)。如果我们将所有文件操作都放在一个大的接口中,那么 TextEditor
和 ImageEditor
就不得不实现它们不需要的方法,违反了接口隔离原则。
通过将接口设计成小而专注的接口,每个接口只包含一个单一的功能或职责,我们遵循了接口隔离原则,并且使得系统更易于理解、扩展和修改。
6、依赖倒置原则
依赖倒置原则(Dependency Inversion Principle,DIP)是面向对象设计中的一个基本原则,由罗伯特·马丁(Robert C. Martin)在他的《敏捷软件开发:原则、模式和实践》(Agile Software Development, Principles, Patterns, and Practices)一书中提出。依赖倒置原则指出,高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
依赖倒置原则的核心思想是通过使用抽象来降低类之间的耦合度,从而使得系统更加灵活、可扩展和易于维护。具体来说,依赖倒置原则要求我们将程序的设计重心放在抽象上,而不是具体实现上,通过使用接口、抽象类或者依赖注入等方式来实现依赖倒置。
在实践中,可以通过以下几点来遵循依赖倒置原则:
- 高层模块不应该依赖于低层模块,二者都应该依赖于抽象。即高层模块和低层模块都应该依赖于同一个抽象接口或抽象类。
- 抽象不应该依赖于具体实现,具体实现应该依赖于抽象。即抽象接口或抽象类不应该依赖于具体实现,而是具体实现应该依赖于抽象接口或抽象类。
- 可以通过依赖注入(Dependency Injection)等方式来实现依赖倒置,将具体实现的创建和注入交给外部,而不是在类内部创建具体实现的对象。
通过遵循依赖倒置原则,可以使得系统更加灵活、可扩展和易于维护,减少类之间的耦合度,提高代码的可复用性和可测试性。
让我们通过一个简单的例子来说明依赖倒置原则。
假设我们有一个简单的电子邮件发送系统,其中包含一个 EmailSender
类用于发送电子邮件。一开始,EmailSender
类直接依赖于具体的邮件服务提供商,比如 Gmail。这个设计违反了依赖倒置原则,因为高层模块 EmailSender
直接依赖于低层模块,即具体的邮件服务提供商。
#include <iostream>// 具体的邮件服务提供商:Gmail
class Gmail {
public:void sendEmail(const std::string& recipient, const std::string& message) {std::cout << "Sending email to " << recipient << " via Gmail: " << message << std::endl;}
};// 邮件发送类
class EmailSender {
private:Gmail gmail;public:void sendEmail(const std::string& recipient, const std::string& message) {gmail.sendEmail(recipient, message);}
};int main() {EmailSender sender;sender.sendEmail("example@example.com", "Hello, this is a test email.");return 0;
}
现在,让我们通过引入抽象来遵循依赖倒置原则。我们可以定义一个抽象的邮件服务接口 EmailService
,并让 Gmail
类实现这个接口。然后,EmailSender
类只依赖于 EmailService
接口,而不是具体的邮件服务提供商。
#include <iostream>// 抽象的邮件服务接口
class EmailService {
public:virtual void sendEmail(const std::string& recipient, const std::string& message) = 0;
};// 具体的邮件服务提供商:Gmail
class Gmail : public EmailService {
public:void sendEmail(const std::string& recipient, const std::string& message) override {std::cout << "Sending email to " << recipient << " via Gmail: " << message << std::endl;}
};// 邮件发送类
class EmailSender {
private:EmailService* emailService;public:EmailSender(EmailService* service) : emailService(service) {}void sendEmail(const std::string& recipient, const std::string& message) {emailService->sendEmail(recipient, message);}
};int main() {Gmail gmail;EmailSender sender(&gmail);sender.sendEmail("example@example.com", "Hello, this is a test email.");return 0;
}
通过这种方式,EmailSender
类不再直接依赖于具体的邮件服务提供商,而是依赖于抽象的邮件服务接口 EmailService
。这样做符合依赖倒置原则,使得系统更加灵活、可扩展和易于维护,因为现在可以轻松地切换不同的邮件服务提供商,而不需要修改 EmailSender
类的代码。
相关文章:
【设计模式】SOLID设计原则
1、什么是SOLID设计原则 SOLID 是面向对象设计中的五个基本设计原则的首字母缩写,它们是: 单一职责原则(Single Responsibility Principle,SRP): 类应该只有一个单一的职责,即一个类应该有且只…...

基于java+springboot+vue实现的智能停车计费系统(文末源码+Lw+ppt)23-30
摘 要 随着人们生活水平的高速发展,智能停车计费信息管理方面在近年来呈直线上升,人们也了解到智能停车计费的实用性,因此智能停车计费的管理也逐年递增,智能停车计费信息的增加加大了在管理上的工作难度。为了能更好的维护智能…...

IntelliJ IDEA 2022.3.2 解决decompiled.class file bytecode version:52.0(java 8)
1 背景 使用idea 打开一个Kotlin语言编写的demo项目,该项目使用gradle构建。其gradle文件如下: plugins {id javaid org.jetbrains.kotlin.jvm version 1.8.20 } group me.administrator version 1.0-SNAPSHOTrepositories {mavenCentral()jcenter()…...

C++11 设计模式1. 模板方法(Template Method)模式学习。UML图
一 什么是 "模板方法(Template Method)模式" 在固定步骤确定的情况下,通过多态机制在多个子类中对每个步骤的细节进行差异化实现,这就是模板方法模式能够达到的效果。 模板方法模式属于:行为型模式。 二 &…...

HarmonyOS实战开发-自定义分享
介绍 自定义分享主要是发送方将文本,链接,图片三种类型分享给三方应用,同时能够在三方应用中展示。本示例使用数据请求 实现网络资源的获取,使用屏幕截屏 实现屏幕的截取,使用文件管理 实现对文件,文件目录的管理&…...

Spring源码刨析之配置文件的解析和bean的创建以及生命周期
public void test1(){XmlBeanFactory xmlBeanFactory new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));user u xmlBeanFactory.getBean("user",org.xhpcd.user.class);// System.out.println(u.getStu());}先介绍一个类XmlBeanFac…...

如何使用 Grafana 监控文件系统状态
当 JuiceFS 文件系统部署完成并投入生产环境,接下来就需要着手解决一个非常重要的问题 —— 如何实时监控它的运行状态?毕竟,它可能正在为关键的业务应用或容器工作负载提供持久化存储支持,任何小小的故障或性能下降都可能造成不利…...

智能革命:未来人工智能创业的天地
智能革命:未来人工智能创业的天地 一、引言 在这个数字化迅速变革的时代,人工智能(AI)已经从一个边缘科学发展成为推动未来经济和社会发展的关键动力。这一技术领域的飞速进步,不仅影响着科技行业的每一个角落,更是为创业者提供了…...

4月14日总结
java学习 一.多线程 简介:多线程是计算机科学中的一个重要概念,它允许程序同时执行多个任务或操作。在单个程序内部,多线程使得代码可以并行执行,从而提高程序的性能和响应速度。 这里先来介绍一下创建多线程的几种方法。 1.扩展…...
kafka---broker相关配置
一、Broker 相关配置 1、一般配置 broker.id 当前kafka服务的sid(server id),在kafka集群中,该值是唯一的(unique),如果未设置此值,kafka会自动生成一个int值;为了防止自动生成的值与用户设置…...

【Golang学习笔记】从零开始搭建一个Web框架(二)
文章目录 模块化路由前缀树路由 前情提示: 【Golang学习笔记】从零开始搭建一个Web框架(一)-CSDN博客 模块化路由 路由在kilon.go文件中导致路由和引擎交织在一起,如果要实现路由功能的拓展增强,那将会非常麻烦&…...

高精度地图导航论文汇总
文章目录 2021基于车载激光点云的高精地图矢量化成图[J] 2022基于高精度地图的智能车辆路径规划与跟踪控制研究[M] 2023一种无人驾驶融合决策方案的设计与实现[M] 2021 基于车载激光点云的高精地图矢量化成图[J] 摘要: 针对车载激光点云中对各特征物提取结果后矢量…...

【域适应】基于域分离网络的MNIST数据10分类典型方法实现
关于 大规模数据收集和注释的成本通常使得将机器学习算法应用于新任务或数据集变得异常昂贵。规避这一成本的一种方法是在合成数据上训练模型,其中自动提供注释。尽管它们很有吸引力,但此类模型通常无法从合成图像推广到真实图像,因此需要域…...
从零实现诗词GPT大模型:pytorch框架介绍
专栏规划: https://qibin.blog.csdn.net/article/details/137728228 因为咱们本系列文章主要基于深度学习框架pytorch进行,所以在正式开始之前,现对pytorch框架进行一个简单的介绍,主要面对深度学习或者pytorch还不熟悉的朋友。 一、安装pytorch 这一步很简单,主要通过p…...

[目标检测] OCR: 文字检测、文字识别、text spotter
概述 OCR技术存在两个步骤:文字检测和文字识别,而end-to-end完成这两个步骤的方法就是text spotter。 文字检测数据集摘要 daaset语言体量特色MTWI中英文20k源于网络图像,主要由合成图像,产品描述,网络广告(淘宝)MS…...

Windows环境下删除MySQL
文章目录 一、关闭MySQL服务1、winR打开运行,输入services.msc回车2、服务里找到MySQL并停止 二、卸载MySQL软件1、打开控制模板--卸载程序--卸载MySQL相关的所有组件 三、删除MySQL在物理硬盘上的所有文件1、删除MySQL的安装目录(默认在C盘下的Program …...
uniapp:uview-plus的一些记录
customStyle 并不是所有的组件都有customStyle属性来设置自定义属性,有的还是需要通过::v-deep来修改内置样式 form表单 labelStyle 需要的是一个对象 :labelStyle"{color: #333333,fontSize: 32rpx,fontWeight: 500}"dateTimePicker选择器设置默认值…...

OLTP 与 OLAP 系统说明对比和大数据经典架构 Lambda 和 Kappa 说明对比——解读大数据架构(五)
文章目录 前言OLTP 和 OLAPSMP 和 MPPlambda 架构Kappa 架构 前言 本文我们将研究不同类型的大数据架构设计,将讨论 OLTP 和 OLAP 的系统设计,以及有效处理数据的策略包括 SMP 和 MPP 等概念。然后我们将了解经典的 Lambda 架构和 Kappa 架构。 OLTP …...

步骤大全:网站建设3个基本流程详解
一.领取一个免费域名和SSL证书,和CDN 1.打开网站链接:https://www.rainyun.com/z22_ 2.在网站主页上,您会看到一个"登陆/注册"的选项。 3.点击"登陆/注册",然后选择"微信登录"选项。 4.使用您的…...

利用Sentinel解决雪崩问题(二)隔离和降级
前言: 虽然限流可以尽量避免因高并发而引起的服务故障,但服务还会因为其它原因而故障。而要将这些故障控制在一定范围避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级手段了,不管是线程隔离还是熔断降级,都是对客户端(调…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...