DDD该怎么去落地实现(3)通用的仓库和工厂
通用的仓库和工厂
我有一个梦,就是希望DDD能够成为今后软件研发的主流,越来越多研发团队都转型DDD,采用DDD的设计思想和方法,设计开发软件系统。这个梦想在不久的将来是有可能达成的,因为DDD是软件复杂性的解决之道,而今后的软件会越来越庞大,越来越复杂。然而,经过这几年的DDD热潮,阻碍各研发团队转型DDD的拦路虎是什么呢?我认为是DDD落地开发编码过于复杂,编码工作量大,阻碍了DDD的推广。试想,一个新的开发思想能降低研发的工作量,必然得到大家的普遍欢迎,推行就比较容易,而反之则非常困难,抵触情绪大。
那么,为什么DDD落地开发的成本很高,编码的工作量大呢?针对这个问题,我们要好好分析分析,进而找到解决的思路。按照DDD的开发规范,当团队经过一系列的业务梳理,形成领域模型以后,就开始以领域模型为核心,设计开发业务系统。如图所示是DDD的分层架构:
通过这张图可以看到,展现层就是前端界面(如WebUI、客户端程序、移动App等等),应用层就是Controller,领域层的服务就是Service,聚合、实体、值对象都是从领域模型中映射过来的领域对象,其它都是基础设施(其中也包括了上一期提到的仓库、工厂、缓存)。这样一个架构集中体现了“整洁架构”的设计思想,即将上层的业务代码与底层的技术框架通过分层进行分离,进而实现解耦。也就是说,只有领域层的服务Service、领域对象(聚合、实体、值对象)才能编写业务代码,实现业务规则、业务操作、业务流程,是领域模型的映射。其它层次的代码都是技术,包括前端UI、应用层的Controller、基础设施,以及这张图没有画出来的服务网关、数据库,等等。按照这样的设计思想,最后代码编写的效果就变成了这样:
在整个系统中,每个功能都必须要有各自的Controller、Service和该功能所需的领域对象(聚合、实体、值对象),然后在持久化到数据库的时候,还得有对应的仓库、工厂、缓存。并且,每个功能的仓库、工厂、缓存都不一样。譬如,订单仓库中存在聚合,在增删改订单表的同时,还有增删改订单明细表,而库存仓库只需要管库存表的增删改;订单工厂在查询的时候需要装配与订单相关的客户、地址、订单明细,而库存工厂只需要装配与库存相关的商品对象。正因为如此,每个功能在开发的时候,都需要编写大量代码。
不仅如此,在不同层次中,数据是存储在不同格式的数据对象中,因此数据在各个层次中流转时,还要编写各种格式的数据对象及转换程序,在Json, DTO, DO与PO中转换数据。这样一套下来,DDD的软件开发就麻烦死了,如果我是程序员,我也会不胜其烦。这就是DDD目前的问题所在。
很多时候就是这样的,当分析和查找到问题以后,就离解决问题不远了。我的思路就是,通过一个底层平台(如低代码平台)将DDD中那些繁杂的操作统一起来,实现集约化,那么开发人员就只需要去编写那些各自的业务代码,那么工作量不就变小了吗?也就是说,如果数据接入层、应用层、基础设施层都通过平台实现了,开发人员就只需要编写领域层的领域服务Service和领域对象(聚合、实体、值对象),以及前端的UI界面,开发人员的工作量就减少了,就可以更加专注地按照领域模型去设计编码,DDD不就更容易落地了。
这的确是一个非常完美的思路,然而要实现这个思路,很显然需要一个支持DDD的强大底层平台。那么,这个强大的底层平台需要提供哪些功能呢?我认为有3个:通用的Controller、仓库及其工厂。按照CQRS(Command Query Responsibility Segregation)架构的设计思想,该平台可以划分成两部分设计:增删改(即命令)和查询,我们先看看增删改的设计思路。
在业务系统实现增删改的操作(即命令操作)时,和过去一样,每个功能在前端都有各自的UI。但和过去不一样的是,所有的UI在请求后端时,都是请求的那一个Controller(即OrmController)。前端请求的Url中包含了要请求的功能,因此这个Controller通过反射去请求后端的Service,并自动将Json转换为领域对象。这里有一个很神奇的事情就是,这个Controller怎么能自动将Json转换为领域对象呢?我们可以在开发规范上规定,前端请求后端时,Json对象必须与后台的领域对象保持一致。如前端提交订单,其Json对象长这样:
{"id": 1,"customerId": 10001,"addressId": 1000100,"orderItems": [{"id": 10,"productId": 30001,"quantity": 2}]
}
可以看到,Json对象不必包含领域对象的所有属性,而是必要属性。后端的OrmController收到这个Json以后,就可以通过DDD工厂去读取DSL,将Json转换为订单对象,并请求OrderService中的create()方法,就可以完成创建订单的操作。当然,如果用户请求的是“下单”,要做的就不仅仅是创建订单,还有支付、库存扣减等操作,需要分布式事务,因此请求的是OrderAggService的placeOrder()方法,详细的设计详见测试用例:
OrderService的测试用例
OrderAggService的测试用例
接着,整个业务操作都在领域层的Service和领域对象中进行(详见《充血模型 or 贫血模型》)。当所有的业务操作的执行完以后,通过仓库进行数据持久化。这时,所有的Service都注入了通用仓库,由它去完成相关的增删改操作(详见上一期的通用仓库设计思路)。
有了DDD的底层平台的支持,所有的领域对象和Service完成的都是对业务的操作,它们只知道领域对象长什么样,只对领域对象进行操作,并不知道后面有数据库,从而实现整洁架构中业务与技术的解耦。接着,将增删改等数据库操作交给底层的通用仓库,包括聚合关系的增删改,实现了CQRS中的“C”。
那么,领域对象的查询(即CQRS中的“Q”)又该如何实现呢?按照DDD的设计思想,当我们将领域模型中对象的关系映射到程序中领域对象的关系以后,在查询领域对象时,底层也要保持这种关系。也就是说,当查询订单时,底层不仅仅是查询订单表,还要查询与订单相关的用户表、地址表与订单明细表,最后将它们装配成一个完整的订单对象。这个查询与装配的工作就交给了DDD的“工厂”来完成。
同样,过去的DDD编码实现,需要为每个领域对象编写工厂,来完成这个查询与装配的工作,这无疑会增加DDD的开发工作量。为了简化DDD的编码,降低落地难度,我们的思路同样是由底层提供一个通用的工厂,所有对领域对象的查询统统都交给这个通用工厂。当通用工厂要查询数据时,先查找DSL获取该对象对应的数据库表与所有的关系。然后,通用工厂根据这些信息,依次到数据库各对应表中去进行查询。最后,再依据DSL进行装配,返回一个完整的领域对象。这个领域对象在返回给Service前,还会在仓库中进行缓存,以提高下次查询的效率。所以,通用工厂不直接面向Service,而是被通用仓库封装。通用仓库封装了通用仓库与缓存,就可以完成上层Service的所有增删改与查询的操作。
譬如,现在要实现对订单的查询,首先通过MyBatis去编写一个mapper:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.edev.emall.query.dao.CustomerMapper"><sql id="select">SELECT * FROM t_customer WHERE 1 = 1</sql><sql id="conditions"><if test="id != '' and id != null">and id in (${id})</if><if test="value != '' and value != null">and (id like '%${value}' or name like '%${value}')</if><if test="gender != '' and gender != null">and gender = #{gender}</if></sql><sql id="isPage"><if test="size != null and size !=''">limit #{size} offset #{firstRow} </if></sql><select id="query" parameterType="java.util.HashMap" resultType="java.util.HashMap"><include refid="select"/><include refid="conditions"/><include refid="isPage"/></select><select id="count" parameterType="java.util.HashMap" resultType="java.lang.Long">select count(*) from (<include refid="select"/><include refid="conditions"/>) count</select><select id="aggregate" parameterType="java.util.HashMap" resultType="java.util.HashMap">select ${aggregation} from (<include refid="select"/><include refid="conditions"/>) aggregation</select>
</mapper>
在这个mapper中可以看到,对订单的查询就只查询订单表,而不进行其它任何的关联。接着,在spring中进行如下的装配:
@Configuration
public class QryConfig {@Autowired @Qualifier("basicDaoWithCache")private BasicDao basicDaoWithCache;@Autowired @Qualifier("repositoryWithCache")private BasicDao repositoryWithCache;@Beanpublic QueryDao customerQryDao() {return new QueryDaoMybastisImplForDdd("com.edev.emall.customer.entity.Customer","com.edev.emall.query.dao.CustomerMapper");}@Beanpublic QueryService customerQry() {return new AutofillQueryServiceImpl(customerQryDao(), repositoryWithCache);}@Beanpublic QueryDao accountQryDao() {return new QueryDaoMybastisImplForDdd("com.edev.emall.customer.entity.Account","com.edev.emall.query.dao.AccountMapper");}@Beanpublic QueryService accountQry() {return new AutofillQueryServiceImpl(accountQryDao(), basicDaoWithCache);}
}
先装配一个QueryDao,它有一个QueryDaoMybastisImplForDdd的实现类,通过MyBatis对订单进行查询,然后按照DDD返回订单对象的列表。接着,再注入到QueryService中,它有一个AutofillQueryServiceImpl的实现类。这样,当QueryDao通过分页查询出这一页的20条记录以后,AutofillQueryServiceImpl就会根据DSL进行数据补填,将这每一个订单对象的用户、地址、明细,都到数据库中进行查询,然后完成补填与装配,最后获得一个完整的领域对象列表。有了这样的设计,每一个模块的查询都变得简单了。你只需要按照领域模型先形成领域对象和DSL,然后编写一个MyBatis的mapper,进行spring的装配,查询的开发工作就完成了。注意,AutofillQueryServiceImpl的第二个参数是在进行补填时,用谁来查询并补填。如果要补填的对象里还有关系(如地址里有省、市、县的关联),则选择repositoryWithCache,否则就用basicDaoWithCache。
最后,每个查询功能都有各自的UI界面,但它们在查询时,请求的都是这一个Controller(即QueryController)。这样,通过领域建模,通过一些简单的配置就可以快速完成各个模块的查询功能。
有了这个平台,按照DDD的设计思想,我们只需要进行领域建模,将我们对业务的理解形成领域模型,就可以快速完成软件的开发。如今有了AI编程,甚至可以训练一个Agent,我们只要深入地理解业务,形成领域模型,就可以让AI按照这样的思路快速开发系统。有了这样的思路,不仅可以让我们将更多的精力放到业务理解而不是软件开发,给我们减负,又可以给AI编程制定规范,有利于日后长期的维护与变更。相信在这样的背景下,DDD又可以焕发生机,成为日后软件开发的主流。
(待续)
相关文章:
DDD该怎么去落地实现(3)通用的仓库和工厂
通用的仓库和工厂 我有一个梦,就是希望DDD能够成为今后软件研发的主流,越来越多研发团队都转型DDD,采用DDD的设计思想和方法,设计开发软件系统。这个梦想在不久的将来是有可能达成的,因为DDD是软件复杂性的解决之道&a…...
sql sqlserver的特殊函数COALESCE和PIVOT的用法分析
一、COALESCE是一个返回参数中第一个非NULL值的函数, 列如:COALESCE(a,b,c,d,e);可以按照顺序取abcde,中的第一个非空数据,abcde可以是表达式 用case when 加ISNULL也可以实现,但是写法复杂了…...
FPGA简介|结构、组成和应用
Field Programmable Gate Arrays(FPGA,现场可编程逻辑门阵列),是在PAL、GAL、CPLD等可编程器件的基础上进一步发展的产物, 是作为专用集成电路(ASIC)领域中的一种半定制电路而出现的,…...
Vue2/Vue3生命周期对比
Vue2的生命周期钩子 beforeCreate 在实例初始化之后,数据观测(data)和事件配置之前调用。此时无法访问 data、methods 等。 created 在实例创建完成后调用。此时可以访问 data、methods,但 DOM 还未生成。 beforeMount 在挂载…...
Spring Boot 携手 DeepSeek:开启智能交互新时代
前言 在当今数字化浪潮汹涌澎湃的时代,人工智能技术正以前所未有的速度改变着我们的生活和工作方式。大语言模型作为人工智能领域的一颗璀璨明星,凭借其强大的自然语言处理能力,为各个行业带来了新的发展机遇。DeepSeek 作为一款性能卓越的大语言模型,以其高效、准确的文本…...
【Elasticsearch】Mapping概述
以下是Elasticsearch中提到的关于Mapping的各模块概述: --- 1.Dynamic mapping(动态映射) 动态映射是指Elasticsearch在索引文档时,自动检测字段类型并创建字段映射的过程。当你首次索引一个文档时,Elasticsearch会根…...
国内Ubuntu离线安装和配置Ollama服务
以下是在 Ubuntu 22.04 系统上,安装Ollama 的完整安装和配置步骤: 1. 准备工作 确保你具备 root 权限,并安装了必要的工具,如 tar、systemctl 等。 2. 创建 Ollama 用户和组 创建一个专门的 ollama 用户和组来运行 Ollama 服务…...
极狐GitLab 17.8 正式发布,多项 DevOps 重点功能解读【二】
GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料: 极狐GitLab 官网极狐…...
【开源免费】基于Vue和SpringBoot的旅游管理系统(附论文)
本文项目编号 T 229 ,文末自助获取源码 \color{red}{T229,文末自助获取源码} T229,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…...
Communications link failure异常分析解决
Communications link failure异常分析解决 异常日志分析与解决常见原因解决方法 异常日志 2025-02-05 14:26:58.750 ERROR [aaeae2435a2749ac0c4af65b6e4db68c] [http-nio-9528-exec-6] o.s.t.i.TransactionAspectSupport.completeTransactionAfterThrowing:680 - Application…...
建造者模式构建对象
目录 一、创建类 二、用法 三、完整代码 一、创建类 Builder public class User {private final String name;private final int age;Builder.Default private String address "Unknown";Builder.Default private String email "N/A"; }二、用法 (1…...
2025 (ISC)²CCSP 回忆录
2025.1.20 广州,周一,我一次性通过了CCSP的考试。 为什么要考证? 个人成长所需 职业热情:做一行爱一行,既然我投入了美好的青春年华到网络安全行业当中,那么对于这个行业最有权威的认证,是肯定…...
代码随想录算法训练营第三十九天| 动态规划03
01 背包问题 二维 代码随想录 视频讲解:带你学透0-1背包问题!| 关于背包问题,你不清楚的地方,这里都讲了!| 动态规划经典问题 | 数据结构与算法_哔哩哔哩_bilibili 注意点: 1. dp[i][j] 表示从下标为[0-i]…...
对正则表达式说不!!!
可能大家都会和我一样,时常会遇到正则表达式,有时候会忘记某些字符而苦恼。今天就帮助大家克服它,虽然不多,但我认为掌握这些足够了,万变不离其宗,以不变应万变。 一、正则表达式内容分类 1. 字符类 [abc…...
制作Ubuntu根文件
系列文章目录 Linux内核学习 Linux 知识(1) Linux 知识(2) WSL Ubuntu QEMU 虚拟机 Linux 调试视频 PCIe 与 USB 的补充知识 vscode 使用说明 树莓派 4B 指南 设备驱动畅想 Linux内核子系统 Linux 文件系统挂载 QEMU 通过网络实现…...
工作一个月的经历和总结
目录 背景: 过程: 初来乍到: 小试牛刀: 终结篇: 总结: 背景: 通过朋友介绍,来到秦皇岛戴卡兴龙轮毂有限公司,我的朋友在这已经干了将近2年了,说这里比…...
Linux 更改 SSH 默认端口以提升服务器安全
🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template 🌺 仓库主页: GitCode︱ Gitee ︱ Github 💖 欢迎点赞 👍 收藏 ⭐评论 …...
C#中反射的原理介绍及常见的应用场景介绍
反射(Reflection)是C#中的一种机制,允许程序在运行时获取类型信息并动态调用其成员。通过反射,程序可以访问程序集、模块、类型及其成员(如方法、属性、字段等),并能在运行时创建对象、调用方法…...
Linux内核中IPoIB驱动的初始化机制深度解析
IP over InfiniBand(IPoIB)是Linux内核中重要的网络协议,允许在InfiniBand(IB)网络上传输IP数据包,广泛应用于高性能计算和存储场景。本文深入分析IPoIB驱动初始化流程,探讨其核心设计及关键技术实现。 一、初始化流程概览 IPoIB驱动的初始化通过ipoib_ndo_init函数入口…...
深度学习框架PyTorch
第一章:机器学习基本概念(附代码) 第二章:KNN算法:从思想到实现(附代码) 第三章:决策树算法:从思想到实现(附代码) 第四章:机器学习简…...
TCP可靠传输的ARQ协议
基本知识 ARQ(Automatic Repeat-reQuest)协议主要包含:停等ARQ协议、连续ARQ协议,其中连续ARQ协议是为了解决停等ARQ协议信道利用率低的问题,目前传统的连续ARQ协议有回退N帧ARQ协议、选择性重传ARQ协议。 注意&#…...
Windows环境安装Kafka(集群版)
大家好,最近在准备Java面试,复习到Kafka的相关知识,一时兴起,就想在自己的Windows笔记本上安装一个Kafka集群。下面就记录一下安装步骤。 工具分享 Offset Explorer:Kafka可视化工具 下载地址:https://ww…...
服务器虚拟化(详解)
服务器虚拟化是一种技术,通过将物理服务器的硬件资源(如CPU、内存、存储、网络等)抽象化并分割成多个虚拟机(VM),每个虚拟机可以独立运行不同的操作系统和应用程序。虚拟化使得资源使用更加高效,…...
今日写题work05
题目:用队列实现栈 思路 队列的特点是先进先出,而栈的特点是后进先出。所以想要用队列实现模拟栈,我们可以使用两个队列,一个队列负责压栈,一个队列负责出栈。压栈很简单就是检空再调用队列的push就好,那出…...
DeepSeek模型架构及优化内容
DeepSeek v1版本 模型结构 DeepSeek LLM基本上遵循LLaMA的设计: 采⽤Pre-Norm结构,并使⽤RMSNorm函数. 利⽤SwiGLU作为Feed-Forward Network(FFN)的激活函数,中间层维度为8/3. 去除绝对位置编码,采⽤了…...
145,【5】 buuctf web [GWCTF 2019]mypassword
进入靶场 修改了url后才到了注册页面 注测后再登录 查看源码 都点进去看看 有个反馈页面 再查看源码 又有收获 // 检查$feedback是否为数组 if (is_array($feedback)) {// 如果是数组,弹出提示框提示反馈不合法echo "<script>alert(反馈不合法);<…...
Beszel监控Docker安装
一、Beszel Hub安装 #Beszel Hub安装 mkdir -p ./beszel_data && \ docker run -d \--name beszel \--restartunless-stopped \-v ./beszel_data:/beszel_data \-p 8090:8090 \henrygd/beszel#创建账号 账号/密码:adminadmin.com/adminadmin.com 二、Besz…...
Ansible批量配置服务器免密登录步骤详解
一、准备工作 192.168.85.138 安装ansible,计划配置到139的免密 192.168.85.139 待配置免密 1. 生成SSH密钥对 在Ansible控制节点生成密钥对,用于后续免密认证: ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa 全部回车默认,无…...
使用llama.cpp在gpu和cpu上运行deepseek-r1 7b的性能对比
使用deepseek-r1 7b模型的q5km量化版本进行测试, gpu上的token解码速度是cpu的近8倍. 测试环境: ubuntu22.04 x86llama.cpp cpu intel 10750h 4.41 tokens / s model size params backend threads test t/s qwen2 7B Q5_K - Medium 5.07 GiB 7.62 B CPU 6 pp512 …...
1.【BUUCTF】[SUCTF 2019]EasyWeb
进入题目页面如下 给出源码开始代码审计 <?php // 定义一个名为 get_the_flag 的函数,该函数主要处理文件上传逻辑 function get_the_flag(){// 构造用户上传文件的目录,目录名是 "upload/tmp_" 加上客户端 IP 地址的 MD5 哈希值$userdir…...
GMSL 实例1:当 MAX96717 遇上 MAX96724,打通 Camera 视频数据传输
新年伊始,继 Deepseek 在 AI 圈掀起风波之后。比亚迪在2月10日发布会上重磅官宣:全系车型将搭载自研的高阶智驾系统“天神之眼”,覆盖从10万元级入门车型到高端豪华车型的所有范围。此举如一颗重磅炸弹投向当前一卷再卷的新能源汽车赛道&…...
2025有哪些关键词优化工具好用
越来越多的企业和个人开始意识到搜索引擎优化(SEO)对于网站曝光和业务增长的重要性。在SEO优化的过程中,关键词优化占据着至关重要的地位。关键词是用户在搜索引擎中输入的词语,优化关键词有助于提高网站在搜索结果中的排名&#…...
Python常见面试题的详解6
1. 按字典 value 值排序 要点:对于给定字典,使用 sorted() 函数结合 items() 方法,依据 value 进行排序,也可以定义一个通用函数,支持按 value 升序或降序排序。示例: python d {a: 1, b: 2, c: 3, d: …...
宝塔和docker的区别
宝塔面板 vs Docker:区别、场景与互补使用指南 eepseek回答宝塔和docker的区别 好的,我现在需要回答用户关于宝塔面板和Docker之间的区别的问题。首先,我需要明确这两个工具各自的定义、功能以及它们适用的场景,这样才能准确比较…...
vscode调试redis
系统:ubuntu redis:redis-6.0.3 1.在vs中安装c/c编译插件 2.用vscode打开redis-6.0.3 3.在菜单中找到run->Add Configuration… 4.会在目录中生成一个./vscode目录,里面包含launch.json,修改launch.json中的program:${workspaceFolder}…...
tcp/ip协议设置参数,tcp/ip协议6设置
TCP/IP协议设置参数主要涉及到IP地址、子网掩码、网关地址以及DNS服务器地址等关键参数。这些参数的配置确保了网络设备能够正确地接入互联网并与其他设备进行通信。以下是对这些参数设置的详细说明: 1. IP地址 定义:IP地址是互联网中用于唯一标识每一…...
计算机网络原理试题二
一、单选 1.(单选题,5分)以下关于计算机网络定义的描述中,错误的是 A.以能够相互共享资源的方式互联起来的自治计算机系统的集合 B.计算机资源主要指计算机的CPU、内存和操作系统 C.互联的计算机既可以联网工作,也可以脱网单机工作 D.联网计算机之间的通信必须遵循…...
#渗透测试#批量漏洞挖掘#致远互联AnalyticsCloud 分析云 任意文件读取
免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停…...
企业文件共享中的权限管理与安全风险防范
在企业的日常运营中,文件共享是必不可少的一项工作。然而,文件共享过程中如果权限管理不当,极易引发安全风险,导致企业敏感信息泄露。因此,加强文件共享中的权限管理与安全风险防范,对于保障企业信息安全至…...
2025智能硬件售后服务管理系统选择的六大标准
2025智能硬件售后服务管理系统选择的六大标准 随着2025年的到来,智能硬件行业正以前所未有的速度发展,产品迭代加速,用户需求日益多样化。在这一背景下,售后服务管理系统的选择成为了智能硬件厂商能否在激烈的市场竞争中脱颖而出…...
C++ references
C复杂在于提供了太多的内存模型 或者说: 提供了两种东西: 1、可以放对象的地方: 堆栈堆全局数据区里 2、提供了很多访问对象的方式 变量是对象指针访问对象引用来访问对象 3 * 3 9 种访问对象的方式 规则 引用一般初始化࿰…...
STM32 RCC功能说明 复位和时钟控制RCC
目录 背景 RCC配置时钟主要涉及两方面 程序 第1步、RCC默认初始化 第2步、等待HSE工作稳定 第3步、设置PLL时钟源以及倍频数 第4步、设置AHB总线时钟(HCLK) 第5步、设置PCLK1(APB1总线) 第6步、设置PCLK2(APB2总线) 第7步、FLASH存储器的配置 …...
2024年终总结和2025年规划
2024年的主线是AI基础的学习和读书,虽然AI学习花费了更多的时间,但是读书长久看来于我是更重要的事情,哈哈哈,因此先简单回顾一下读书记忆,回顾我的2024,再展望一下我的2025. 我的2024年记忆 读万卷书&am…...
Day2 25/2/15 SAT
【一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解(马士兵)】https://www.bilibili.com/video/BV13g41157hK?p4&v…...
新版电脑通过wepe安装系统
官方下载链接 WIN10下载 WIN11下载 微PE 启动盘制作 1:选择启动盘的设备 2:选择对应的U盘设备,点击安装就可以,建议大于8g 3:在上方链接下载需要安装的程序包,放入启动盘,按需 更新系统 …...
分享一个使用的音频裁剪chrome扩展-Ringtone Maker
一、插件简介 铃声制作器是一个简单易用的 Chrome 扩展,专门用于制作手机铃声。它支持裁剪音频文件的特定片段,并将其下载为 WAV 格式,方便我们在手机上使用。无论是想从一段长音频中截取精彩部分作为铃声,还是对现有的音频进行个…...
Managed Lustre 和 WEKA:高性能文件系统的对比与应用
Managed Lustre 和 WEKA:高性能文件系统的对比与应用 1. 什么是 Managed Lustre?主要特点:适用场景: 2. 什么是 WEKA?主要特点:适用场景: 3. Managed Lustre 和 WEKA 的对比4. 如何选择 Managed…...
自己制作网站教程方法
SaaS建站,作为一种创新的网站构建模式,通过将网站建设所需的复杂软硬件资源整合到云端,为用户带来了前所未有的便捷性、高效性和灵活性,彻底颠覆了传统网站建设的固有模式,为用户提供了一个更为理想且适应性强的建站解…...
基于角色访问控制的UML 表示02
一个用户可以成为很多角色的成员,一个角色可以有许多用户。类似地,一个角色可以有多个权限,同一个权限可以被指派给多个角色。每个会话把一个用户和可能的许多角色联系起来。一个用户在激发他或她所属角色的某些子集时,建立了一个…...
平面与平面相交算法杂谈
1.前言 空间平面方程: 空间两平面如果不平行,那么一定相交于一条空间直线, 空间平面求交有多种方法,本文进行相关讨论。 2.讨论 可以联立方程组求解,共有3个变量,2个方程,而所求直线有1个变量…...