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

【笔记】DDD领域驱动设计

同名读书笔记,对于一些自觉重要的点进行记录。

扩展资源:github.com/evancyz/ddd-learning

  • UML中类图的一些基本知识 - jack_Meng - 博客园

最后的第四部分暂时没看


Part Two 模型驱动设计的构造块

Chapter 5 软件中所表示的模型

5.2 模式:Entity(Reference Object)

一个对象主要不是由他们的属性定义,而是有一个“标识”,但又不是如Java自带的标识(==)的形式,而是业务上的,如银行结算单和交易记录簿进行对账时就是具备相同标识的交易。Entity最基本的职责是确保连续性,以便其行为更清楚且可预测。与标识有关的属性尽量留在Entity内。当然实际上这种标识仍是由属性来实现。

5.3 模式:Value Object

eg. 画画时很容易区分出画(Entity)是谁画的,但有两只一样的笔,我们不会记住某个线条是其中哪只笔(VO)画的。

VO:用来描述领域的某个方面而本身没有概念标识的对象。(我们只关心它们是什么,而不关心它们是谁。)

但同一个对象在不同的场景下可能是Entity,也可能是VO,要具体分析。也不一定是Entity has VO,也可能是VO has Entity。如果说Entity之间的双向关联很难维护,那么两个VO之间的双向关联则完全没有意义。

VO应该是不可变的,不需要分配标识,VO所包含的属性应该形成一个概念整体。

5.4 模式:Service

有时候,对象不是一个事物。有些重要的领域操作无法放到Entity或VO中,因为这些操作本质上是一些活动或动作,而非事物。

Service不具有封装的状态,它强调的是与其他对象的关系。Servie的特征:

  1. 与领域概念相关的操作不是Entity或VO的一个自然组成部分。
  2. 接口是根据领域模型的其他元素定义的。
  3. 操作是无状态的。(任何客户都可以使用某个Service的任何实例)

定义接口时要使用模型预言,并确保操作名称是UBIQUITOUS LANGUAGE中的术语。

可根据是否包含专业业务规则将Service分为“应用Service”和“领域Service”。

5.5 模式:Module 模块(或Package)

Module之间应该是低耦合的,Module内部则是高内聚的。

打包时注意:对象的一个基本概念时将数据和操作这些数据的逻辑封装到一起。

除非真正有必要将代码分布到不同的服务器上,否则就把实现单一概念对象的所有代码放在同一个模块中。

5.6 建模范式

对象范式

混合范式

Chapter 6 领域对象的生命周期

6.1 模式:Aggregate

eg. 当删除一个用户时,我们如何处理地址呢?可能还有其他人住在同一个地址,删除的话,其他用户变为引用空对象。保留的话,垃圾地址会积累其他。——在具有复杂关联的模型中,要想保证对象更改的一致性是很困难的。

首先需要一组抽象来封装模型中的引用。Aggregate就是一组相关对象的集合,我们把它作为数据修改的单元。每个Aggregate都有一个根(root)和一个边界(boundary)。外部对象只可以引用根,而边界内部的对象之间可以互相引用。

  • Aggregate内部的对象可以保持对其他Aggregate根的引用。
  • 删除操作必须一次删除Aggregate边界之内的所有对象。

参考P85示例。

6.2 模式:Factory

对象的功能主要体现在其复杂的内部配置以及关联方面。我们应该一直对对象进行提炼,直到所有与其意义或在交互中的角色无关的内容被完全剔除为止。

eg. 汽车只有在被生产时才需要装配工,在我们驾驶时并不需要,由于汽车的装配和驾驶永远不会同时发生,将这两种功能合并到同一个机制中毫无价值。——将创建复杂对象的实例和Aggregate的职责转移给单独的对象,即Factory。

接口的设计。当设计Factory的方法签名时,需要注意:

  • 每个操作都必须是原子的。必须确定创建失败时的操作。
  • Factory将与其参数发生耦合。若只是将参数插入,依赖度是适中的,若是选出一部分在构造对象时使用,耦合将更为紧密。

由于Entity需要“标识”,Entity Factory和VO Factory有所不同。

重建已存储的对象时也需要注意。

6.3 模式:Repository

无论对象执行什么操作,都需要保持对它的引用,那么如何获得这个引用呢?采用关系型数据库使得人民自然而然地使用基于对象的属性来获取引用。——执行查询来找到对象,然后重建它。

从概念上讲,对象检索发生在Entity生命周期的中间,我们把用已存储的数据创建实例的过程称为重建

大多数对象都不应该通过全局搜索来访问。——随意的数据库查询会破坏领域对象的封装和Aggregate。技术基础设施的和数据库访问机制的暴露会增加客户的复杂度,并妨碍模型驱动的设计。

Repository解除了客户的巨大负担,使客户只需要与一个简单的、易于理解的接口进行对话。它们使得应用程序和领域设计与持久化技术解耦。

Factory与Repository:

  • Factory负责处理对象生命周期的开始,而Repository帮助管理生命周期的中间和结束。
  • Factory负责制造新对象,而Repository负责查找已有对象。

引申:《DAO还是Repository,傻傻的分不清?》

关于 Dao 和 Repository 在技术上,假设如果不用区分的那么细。但在语义上就很重要了,因为当我看到 repository 的时候,想到的设计聚合,而 dao 会第一个想到是一个贫血Entity。这也是 模式 的一个重要作用

进一步可以参考:《阿里技术专家详解DDD系列 第三讲 - Repository模式》

传统Data Mapper(DAO)属于“固件”,和底层实现(DB、Cache、文件系统等)强绑定,如果直接使用会导致代码“固化”。所以为了在Repository的设计上体现出“软件”的特性,主要需要注意以下三点:

  1. 接口名称不应该使用底层实现的语法:
  2. 出参入参不应该使用底层数据格式:需要记得的是Repository操作的是Entity对象(实际上应该是Aggregate Root),而不应该直接操作底层的DO。更近一步,Repository接口实际上应该存在于Domain层,根本看不到DO的实现。这个也是为了避免底层实现逻辑渗透到业务代码中的强保障。
  3. 应该避免所谓的“通用”Repository模式

Chapter 7 使用语言:一个扩展的示例

系统设计过程:

  1. 模型设计(UML类图)
  2. 隔离领域:引入应用层
  3. 区分Entity和VO:依次考虑每个对象,看看这个对象是必须被跟踪的实体还是仅表示一个基本值。(或思考两个完全相同的对象是否需要区分开?)
  4. 设计领域类间的关联关系(引用关系)。若存在循环引用,需要考虑数据库设计的问题。
  5. 划分Aggregate边界。
  6. 选择Repository
    1. 上一步有多少Entity是Aggregate的根,则选择存储库的时候只需要考虑这几个实体,其他对象都不能有Repository。
    2. 回看需求,重新确定这些实体确实需要Repository。
  7. 场景走查
  8. 对象的创建:思考是否需要为其中重要的类创建Factory方法。
  9. 重构
    • 并发问题:考虑某项修改操作的使用频率是否会带来对应Aggregate的并发问题。是否可以使用其他技术来替代该操作?eg. 用查询来替代维护一个集合。(又涉及到Repository的问题)。
  10. 划分Module:我们应该寻找紧密关联的概念,并弄清楚我们打算向项目中的其他人员传递什么信息。
  11. 引入新特性(功能)
    1. 链接两个系统(与已有的另一个系统对接)
    2. 进一步完善模型:划分业务。可能需要添加中间类来与其他系统的接口对接。
    3. 性能优化。eg. 上述的中间类可以添加缓存功能来提升性能。
  12. 小结:对于Enterprise Segment的获取:正确的做法是让那些知道划分规则的对象来承担获取这个值的职责,而不是把这个职责施加给包含具体数据(那些规则就作用于这些数据上)的对象。

Part Three 通过重构来加深理解

深层模型:深层模型能够穿透表象,清楚地表达出领域专家们的主要关注点以及最相关的知识。

Chapter 8 通过重构来加深理解

实际上是特定领域模型的理解加深或实际需求增多/复杂化来驱动的重构。而重构的过程本身又会进一步加深对模型的理解。

Chapter 9 将隐式概念转变为显式概念

深层建模的第一步就是要设法在模型中表达出领域的基本概念。开发人员在消化知识或重构的过程中,识别出某个隐含的概念,并将其显式地表达出来。

概念挖掘:与领域专家不断交流。

模式:Specification(举得例子实际就是 调用链模式)

Chapter 10 柔性设计

  • 10.1 模式:Intention-revealing interfaces(释义接口)
    • 如果开发人员为了使用一个组件而必须要去研究它的实现,那么就失去了封装的价值。
    • 在命名类和操作时要描述他们的效果和目的,而不要表露他们是通过何种方式达到目的的。
    • 整个子领域可以被划分到独立的模块中,并用一个表达了其用途的接口把他们封装起来。
  • 10.2 模式:Side-Effect-Free Function(无副作用方法)
    • 我们可以宽泛地把操作分为两个大的类别:命令和查询(P.S. 相当于读和写)
    • 多个规则的相互作用或计算结果的组合所产生的结果是很难预测的。开发人员在调用一个操作时,为了预测操作的结果,必须理解它的实现以及它所调用的其他方法的实现。如果开发人员不得不“揭开接口的面纱”,那么接口的抽象作用就收到了限制。
    • 尽可能把程序的逻辑放到函数中,因为函数是只返回结果而不产生明显副作用的操作。严格地把命令(引起明显的状态改变的方法)隔离到不返回领域信息的、非常简单的操作中。当发现了一个非常适合承担复杂逻辑职责的概念时,就可以把这个复杂逻辑移到Value Object中,这样可以进一步控制副作用。

P. S. 实际上例子是对于两个类A1和A2,将原设计中类A中被改变地部分属性抽出来类B(VO类),即A1、A2分别带有B1、B2,而A1、A2操作时不改变自己的B1、B2,而是生成B3返回。

  • 10.3 模式:Assertion(断言)
    • 如果操作的副作用仅仅是由他们的实现隐式定义的,那么在一个具有大量相互调用关系的系统中,起因和结果会变得一团糟。理解程序的唯一方式就是沿着分支路径来跟踪程序的执行。封装完全失去了价值。跟踪具体的执行也使抽象失去了意义。
    • 把操作的后置条件和类及Aggregate的固定规则表述清楚,如果在你的编程语言中不能直接编写Assertion,那么就把他们编写成自动的单元测试。还可以把他们写到文档或图中(如果符合项目开发风格的话)

P.S. 书中的例子是编写了单元测试。回想一下深度学习Python框架倒是常用assert检查矩阵维度。

  • 10.4 模式:Conceptual Contour(概念轮廓)
    • 如果把模型或设计的所有元素都放在一个整体的大结构中,那么他们的功能就会发生重复。
    • 而另一方面,把类和方法分解开可能也是毫无意义的,这会使客户更复杂,迫使客户对象去理解各个细微部分是如何组合在一起的。
    • 通过反复重构最终会实现柔性设计。
    • 寻找在概念上有意义的功能单元,这样可以使得设计既灵活又易懂。例如,如果领域中对两个对象的“相加”是一个连贯的整体操作,那么就把它作为整体来实现。不要把add()拆分成两个独立步骤。
    • 把设计元素(操作、接口、类和Aggregate)分解为内聚的单元,在这个过程中,你对领域中一切重要划分的直观认识也要考虑在内。在连续的重构过程中观察发生变化和保证稳定的规律性,并寻找能够解释这些变化模式的底层Conceptual Contour。使模型与领域中那些一致的方面(正是这些方面使得领域称为一个有用的知识体系)相匹配。
  • 10.5 模式:Standalone Class(独立的类)
    • 互相依赖使模型和设计变得难以理解、测试和维护。
    • Module和Aggregate的目的都是为了限制互相依赖的关系网。
    • 低耦合是对象设计的一个基本要素。尽一切可能保持低耦合。把其他所有无关概念提取到对象之外。这样类就变得完全独立了,这就使得我们可以单独研究和理解它。
  • 10.6 模式:Closure Of Operation(闭合操作)
    • 在适当的情况下,在定义操作时让它的返回类型和与其参数的类型相同。如果实现者(implementer)的状态在计算中会被用到,那么实现者实际上就是操作的一个参数,因此参数和返回值应该与实现者有相同的类型。这样的操作就是在该类型的实例集合中的闭合操作。闭合操作提供了一个高层接口,同时又不会引入对其他概念的任何依赖。

eg. 从Collection中选择元素子集,需要使用Iterator,但是是否有必要使用Iterator这个额外概念以及其带来的额外复杂性呢?

  • 10.7 声明式设计
    • 通常指一种编程方式——把程序或程序的一部分写成一种可执行的规格(specification)。使用声明式设计时,软件实际上是由一些非常精确的属性描述来控制的。如,可以通过反射机制来实现,或在编译时通过代码生成来实现(根据生命来自动生成传统代码)。这种方法使其他开发人员能够根据字面意义来使用声明。它是一种绝对的保证。
    • 问题:把软件束缚在一个由自动部分构成的框架之;代码生成技术破坏了迭代循环,把生成的代码合并到手写的代码中。
    • 声明式设计发挥的最大价值时用一个范围非常窄的框架来自动处理设计中某个特别单调且易出错的方面,如持久化和对象关系映射。
  • 10.8 声明式设计风格
    • 一旦你的设计中有了Intention-revealing interfaces(释义接口)、Side-Effect-Free Function(无副作用方法)和Assertion(断言),就具备了使用声明式设计的条件。

在之前的Chapter 9 的 Specification中实际使用了调用链,但是调用链只能作为串行的条件过滤使用,如其中所说的为化学品安排容器,但是也应该尽量安排成本低的容器,所以要扩展and、or、not的接口,现在将这几种操作也分别封装成一个类,继承同一个抽象类。

  • 10.9 切入问题的角度

eg. 经过改造,贷款相关业务代码改造为了

  • 复杂的逻辑通过Side-Effect-Free Function被封装到了专门的Value Object中
  • 修改状态的操作很简单,而且是用Assertion来描述的。
  • 模型概念除了耦合,操作只涉及最少的其他类型。

Chapter 11 应用分析模式

分析模式的最大作用是借鉴其他项目的经验,把那些项目中有关设计方向和实现结果的广泛讨论与当前模型的理解结合起来。

关联了Martin Fowler的《分析模式》一书

Chapter 12 将设计模式应用于模型

  • 12.1 模式:Strategy(Policy)策略模式
  • 12.2 模式:Composite 组合模式
    • 定义一个把Composite的所有成员都包含在内的抽象类型。在容器上实现那些查询信息的方法时,这些方法返回由容器内容所汇总的信息。而“叶”节点则给予它们自己的值来实现这些方法。客户只需使用抽象类型,而无需区分“叶”和容器。

组合设计模式

Chapter 13 通过重构得到更深层的理解

重构的时机:

  • 设计没有表达出团队对领域的最新理解
  • 重要的概念被隐藏在设计中了(而且你已经发现了把他们呈现出来的方法)
  • 发现了一个能令某个重要的设计部分变得更灵活的机会

相关文章:

【笔记】DDD领域驱动设计

同名读书笔记,对于一些自觉重要的点进行记录。 扩展资源:github.com/evancyz/ddd-learning UML中类图的一些基本知识 - jack_Meng - 博客园 最后的第四部分暂时没看 Part Two 模型驱动设计的构造块 Chapter 5 软件中所表示的模型 5.2 模式:…...

用AI构建小程序需要多久?效果如何?

随着移动互联网的快速发展,多端应用的需求日益增长。为了提高开发效率、降低成本并保证用户体验的一致性,前端跨端技术在如今的开发界使用已经非常普遍了,技术界较为常用的跨端技术有小程序技术、HTML5技术两大类。 2023年以来,伴…...

深度学习的应用综述

文章目录 引言深度学习的基本概念深度学习的主要应用领域计算机视觉自然语言处理语音识别强化学习医疗保健金融分析 深度学习应用案例公式1.损失函数(Loss Function) 结论 引言 深度学习是机器学习的一个子领域,通过模拟人脑的神经元结构来处理复杂的数据。近年来&…...

whereis命令:查找命令的路径

一、命令简介 ​whereis​ 命令用于查找命令的:可执行文件、帮助文件和源代码文件。 例如 $ whereis ls ls: /usr/bin/ls /usr/share/man/man1/ls.1.gz找到了 ls 命令的可执行文件、帮助文件的位置。 ‍ 二、命令参数 命令格式 whereis [选项] [命令名称]选项…...

【ECMAScript 从入门到进阶教程】第四部分:项目实践(项目结构与管理,单元测试,最佳实践与开发规范,附录)

第四部分:项目实践 第十四章 项目结构与管理 在构建现代 Web 应用程序时,良好的项目结构和管理是确保代码可维护性、高效开发和部署成功的关键因素。这一章将深入讨论项目初始化与配置,以及如何使用构建工具来简化和优化项目建设过程。 14…...

算法讲解—最小生成树(Kruskal 算法)

算法讲解—最小生成树(Kruskal 算法) 简介 根据度娘的解释我们可以知道,最小生成树(Minimum Spanning Tree, MST)就是:一个有 n n n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n n n 个结点…...

掌握 C# 多线程与异步编程

现代应用程序通常需要执行复杂的计算或处理 I/O 操作,这些操作可能会导致主线程阻塞,从而降低用户体验。C# 提供了多线程与异步编程的多种工具,让我们能够高效地并发处理任务。本文将介绍 C# 中的多线程与异步编程,包括 Thread 类…...

Angular 2 用户输入

Angular 2 用户输入 Angular 2 是一个由 Google 维护的开源前端 web 框架,用于构建单页应用程序(SPA)。它以其高效的双向数据绑定、模块化架构和强大的依赖注入系统而闻名。在 Angular 2 应用程序中,处理用户输入是核心功能之一,因为它允许应用程序响应用户的操作。 Ang…...

线程安全的单例模式 | 可重入 | 线程安全 |死锁(理论)

🌈个人主页: 南桥几晴秋 🌈C专栏: 南桥谈C 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据…...

解决方案:梯度提升树(Gradient Boosting Trees)跟GBDT(Gradient Boosting Decision Trees)有什么区别

文章目录 一、现象二、解决方案梯度提升树(GBT)GBDT相同点区别 一、现象 在工作中,在机器学习中,时而会听到梯度提升树(Gradient Boosting Trees)跟GBDT(Gradient Boosting Decision Trees&…...

亚马逊国际商品详情API返回值:电商精准营销的关键

亚马逊国际商品详情API(Amazon Product Advertising API)为开发者提供了一种获取商品信息的方式,这些信息对于电商精准营销至关重要。通过分析API返回的详细数据,商家可以制定更精准的营销策略,提高用户购买转化率。 …...

python爬虫 - 进阶requests模块

🌈个人主页:https://blog.csdn.net/2401_86688088?typeblog 🔥 系列专栏:https://blog.csdn.net/2401_86688088/category_12797772.html 目录 前言 一、SSL证书问题 (一)跳过 SSL 证书验证 &#xff0…...

代码随想录 103. 水流问题

103. 水流问题 #include<bits/stdc.h> using namespace std;void dfs(vector<vector<int>>& mp, vector<vector<int>>& visit, int y, int x){if (visit[y][x] 1) return;visit[y][x] 1;if (y > 0){if (mp[y][x] < mp[y - 1][x…...

数据结构-排序1

1.排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经过排序…...

Springboot 整合 durid

文章目录 Springboot 整合 druiddruid的优势配置参数使用整合 Druid配置数据源配置参数绑定配置参数配置监控页面配置拦截器 Springboot 整合 druid druid的优势 可以很好的监控 DB 池连接 和 SQL 的执行情况可以给数据库密码加密可以很方便的编写JDBC插件 配置参数 使用 整…...

JVM 系列知识体系全面回顾

经过几个月的努力&#xff0c;JVM 知识体系终于梳理完成了。 很早之前也和小伙伴们分享过 JVM 相关的技术知识&#xff0c;再次感谢大家支持和反馈。 最后再次献上 JVM系列文章合集索引&#xff0c;感兴趣的小伙伴可以点击查看。 JVM系列(一) -什么是虚拟机JVM系列(二) -类的…...

crossover软件如何安装程序 及最新图文案张教程

IT之家 2 月 23 日消息&#xff0c;CodeWeavers 近日发布了 CrossOver 24 版本更新&#xff0c;基于近期发布的 Wine 9.0&#xff0c;不仅兼容更多应用和游戏&#xff0c;还初步支持运行 32 位应用程序。 苹果在 macOS Catalina 系统中移除对 32 位软件的支持之后&#xff0c;在…...

Python爬虫之正则表达式于xpath的使用教学及案例

正则表达式 常用的匹配模式 \d # 匹配任意一个数字 \D # 匹配任意一个非数字 \w # 匹配任意一个单词字符&#xff08;数字、字母、下划线&#xff09; \W # 匹配任意一个非单词字符 . # 匹配任意一个字符&#xff08;除了换行符&#xff09; [a-z] # 匹配任意一个小写字母 […...

Jenkins打包,发布,部署

一、概念 Jenkins是一个开源的持续集成工具&#xff0c;主要用于自动构建和测试软件项目&#xff0c;以及监控外部任务的运行。与版本管理工具&#xff08;如SVN&#xff0c;GIT&#xff09;和构建工具&#xff08;如Maven&#xff0c;Ant&#xff0c;Gradle&#xff09;结合使…...

CSS 实现楼梯与小球动画

CSS 实现楼梯与小球动画 效果展示 CSS 知识点 CSS动画使用transform属性使用 页面整体布局 <div class"window"><div class"stair"><span style"--i: 1"></span><span style"--i: 2"></span>…...

sqli-labs less-14post报错注入updatexml

post提交报错注入 闭合方式及注入点 利用hackbar进行注入&#xff0c;构造post语句 unameaaa"passwdbbb&SubmitSubmit 页面报错&#xff0c;根据分析&#xff0c;闭合方式". 确定列数 构造 unameaaa" or 11 # &passwdbbb&SubmitSubmit 确定存在注…...

Python开发环境配置(mac M2)

1. 前言 作为一名程序员&#xff0c;工作中需要使用Python进行编程&#xff0c;甚至因为项目需要还得是不同版本的Python如何手动管理多个版本的Python&#xff0c;如何给Pycharm&#xff08;IDE&#xff09;配置对应的interpreter等&#xff0c;都成为一个 “不熟练工” 的难…...

其他:Python语言绘图合集

文章目录 介绍注意导入数据函数模块画图 介绍 python语言的科研绘图合集 注意 This dataset includes the following (All files are preceded by "Marle_et_al_Nature_AirborneFraction_"):- "Datasheet.xlsx": Excel dataset containing all annual a…...

处理 Vue3 中隐藏元素刷新闪烁问题

一、问题说明 页面刷新&#xff0c;原本隐藏的元素会一闪而过。 效果展示&#xff1a; 页面的导航栏通过路由跳转中携带的 meta 参数控制导航栏的 显示/隐藏&#xff0c;但在实践过程中发现&#xff0c;虽然元素隐藏了&#xff0c;但是刷新页面会出现闪烁的问题。 项目源码&…...

【MySQL】数据目录迁移

一、使用场景 使用该方法一般是数据目录所在磁盘不支持扩展&#xff0c;只能通过新加磁盘来扩展数据目录磁盘空间。通常是Windows服务器&#xff0c;或者是Linux服务器的mysql数据目录的磁盘没有使用lvm。 二、准备工作 1. 新磁盘初始化&#xff0c;达到可使用状态 2. 需要自己…...

【项目安全设计】软件系统安全设计规范和标准(doc原件)

1.1安全建设原则 1.2 安全管理体系 1.3 安全管理规范 1.4 数据安全保障措施 1.4.1 数据库安全保障 1.4.2 操作系统安全保障 1.4.3 病毒防治 1.5安全保障措施 1.5.1实名认证保障 1.5.2 接口安全保障 1.5.3 加密传输保障 1.5.4终端安全保障 资料获取&#xff1a;私信或者进主页。…...

INS淡绿色风格人像街拍Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色介绍 INS 淡绿色风格人像街拍通过 Lightroom 调色可以营造出清新、自然、时尚的视觉效果。这种风格以淡绿色为主色调&#xff0c;给人一种宁静、舒适的感觉。 预设信息 调色风格&#xff1a;INS风格预设适合类型&#xff1a;人像&#xff0c;街拍&#xff0c;自拍&#…...

python 实现最小路径和算法

最小路径和算法介绍 最小路径和问题通常指的是在一个网格&#xff08;如二维数组&#xff09;中&#xff0c;找到从起点&#xff08;如左上角&#xff09;到终点&#xff08;如右下角&#xff09;的一条路径&#xff0c;使得路径上经过的元素值之和最小。这类问题可以通过多种…...

Vue3实现动态菜单功能

文章目录 0.效果演示1.搭建Vue3项目1.1 vite 脚手架创建 Vue3 项目1.2 设置文件别名1.3 安装配置 element-plus1.4 安装配置路由2.登录页面3.后台管理页面3.1 搭建后台框架3.2 左侧菜单栏3.3 header 用户信息3.4 主要内容3.5 footer4.配置静态路由5.记录激活菜单5.1 el-menu 绑…...

Qt+VS2019+大恒相机相机回调方式总结

一、前言 大恒驱动安装完成后&#xff0c;在安装目录有SDK调用文档&#xff0c;里面有更详细的调用介绍&#xff0c;此文档对近期做的Demo做一个回顾性总结。 二、调用流程概述 三、针对性内容介绍&#xff1a; 1. 在执行相机操作之前&#xff0c;需要先执行此代码&#xff1…...