软件设计不是CRUD(8):低耦合模块设计实战——组织机构模块(下)
接上文《软件设计不是CRUD(7):低耦合模块设计实战——组织机构模块(中)》
5、某项目研发团队进行扩展
上文中我们介绍了如何研发一个具有较低耦合强度的组织机构模块(包括模块的SDK和模块的默认本地数据库实现),接着我们就可以在一个应用程序中使用这个组织机构模块了。实际上应用程序研发团队不只是使用这个模块,研发团队还在应用程序开发时收到了客户方的新需求。客户表示需要按照自身情况对组织机构模块功能做如下修改:
- 增加一种组织机构类型,这种组织机构类型可以关联下级组织机构,但是下级组织机构只能是同一类型的组织机构,不能关联任何人员信息。
- 修改目前系统中已存在的组织机构的字段信息,增加“有效时间”、“经办方式”、“预算上限”这几个特定的业务字段。
- 新增一种项目中特定的人员类型和组织机构的关联。
好消息是,这些需求的变化并没有超过组织机构模块的业务维度范畴——组织机构类型的变化和组织机构于人员关联方式的变化,所以需求变化是在设计可控的范畴内。那么我们来看一下组织机构模块是如何支持二次开发满足以上三点需求变化的。
5.1、在应用系统中使用默认的组织机构模块实现
除非项目研发团队想全部自行实现组织机构模块,否则就需要首先在自己的应用系统中引入组织机构模块的默认实现(前者情况只需要引入simple-org-sdk即可)。
- 应用程序Spring-Boot中的pom.xml文件引入组织机构模块的默认实现:
// ......
<dependencies>// ......<!-- 引入组织机构的默认实现 --><dependency><groupId>simple-org</groupId><artifactId>simple-org-local-starter</artifactId><version>0.0.2-SNAPSHOT</version></dependency>// ......
</dependencies>
// ......
从这个示例场景来看,项目研发团队描述的二次开发需求完全可以在默认实现的基础上进行二次开发,而不需要将整个组织机构模块全部进行重新实现。另外基于优先实现接口,而不是优先使用继承的设计要求,应用层面上开发人员的主要二开手段,还是对org-sdk提供的接口进行实现。
上图把本系列第7篇、第8篇文章介绍的和将要介绍的组织机构模块实现分为两部分。其中一部分是在应用程序中所引用的simple-org-local-starter部分,这部分组织机构模块的默认实现,关键的实现细节已经在上文中(《软件设计不是CRUD(7):低耦合模块设计实战——组织机构模块(中)》)进行了介绍。作为二次开发团队只能使用simple-org-local-starter,但不能对simple-org-local-starter进行修改。有的读者就会问,既然不能对simple-org-local-starter进行修改,那怎么进行组织机构模块的扩展实现呢?这就涉及到第二部分:二次开发团队为了实现项目中特定的需求,基于org-sdk提供的接口(和org的本地默认实现)所自行实现的自定义类。下面我们详细看看二次开发团队为了实现定制需求,所进行的具体开发内容。
5.2、进行业务模型的定制开发
5.2.1、定义新的组织机构数据模型
- 定义一个新的模型,来满足“新增一种组织机构模型”的需求(MyOrg),这也是对组织机构模块“组织机构类型”可随意扩展的效果展示:
// 这是本项目中特定使用的组织机构信息
@Getter
@Setter
public class MyOrg implements TreeOrganization {public static final String MY_ORG = "myorg";private String id;// 组织机构类型private String type = MY_ORG;// 业务编号private String code;// 组织机构上级组织机构编号private String parentCode;// 组织机构上级组织机构类型private String parentType;// 中文名// ========== 以下都是这种新的组织机构类型所特有的业务字段private String name;// 组织机构负责人private String main;// 机构级别private String level;// 级别说明private String levelDesc;// 自有资金private BigDecimal funds;// 组织机构携带的下级组织机构信息(组织机构特性字段)private List<? extends Organization> kids;// ====== 注意,该组织机构类型不能关联任何的用户信息@Overridepublic <M extends UserMapping> List<M> getUsers() {return null;}@Overridepublic void setUsers(List<? extends UserMapping> users) {throw new UnsupportedOperationException("该组织机构类型不支持关联用户");}
}
- 注册这个模型(MyOrganizationModuleRegister ):定义好了新的组织机构类型,我们需要把这个组织机构类型注册到IOC容器,并保证组织机构模块主要的逻辑控制中能够识别到这个组织机构类型模型。
// 实现OrganizationModuleRegister接口,保证这个新的组织机构模型能够被正确识别、转换。
@Component
public class MyOrganizationModuleRegister implements OrganizationModuleRegister<MyOrg> {// 默认的组织机构类型,排序优先度很高@Overridepublic int getOrder() {return 2;}@Overridepublic String type() {return MyOrg.MY_ORG;}@Overridepublic Class<? extends MyOrg> getOrgClass() {return MyOrg.class;}@Overridepublic MyOrg transform(JSONObject json) {// 正式生产过程中,可能涉及到递归的下级组织机构转换,// 这里不进行演示,只对基本信息和可能的直属用户信息进行转换// 1、===== 首先进行组织机构基本信息的转换String code = json.getString("code");Validate.notBlank(code , "错误的组织机构编号信息,请检查!");String name = json.getString("name");Validate.notBlank(name , "错误的组织机构名称信息,请检查!");// 还有其他和业务相关的必传字段进行的边界校验// ......// 不处理下级组织机构,也不处理下级用户(因为不允许携带用户)json.remove("kids");json.remove("users");MyOrg myOrg = json.toJavaObject(MyOrg.class);return myOrg;}// 只允许关联组织机构为MyOrg的下级组织机构@Overridepublic String[] enableOrgTypes() {return new String[] {MyOrg.MY_ORG};}// 不允许关联任何类型的用户@Overridepublic String[] enableUserTypes() {return new String[] {};}
}
5.2.2、定义新的组织机构—用户关联模型
- 定义一个新的模型,来满足“新增一种组织机构—用户关联”的需求(MyMappingUser)
// 这是该项目特定的一种用户和组织机构的关联
@Setter
@Getter
public class MyMappingUser implements UserMapping {public static final String MY_MAPPING_ORG = "mapping_user";private String id;// 唯一的用户账号信息private String account;// 用户中文姓名信息private String describer;// 用户昵称信息private String name;// ========= 以下是这种新的组织机构-用户关联信息中,特有的业务字段// 特定关联信息1private String field1;// 特定关联信息2private Long field2;// 特定关联信息3private BigDecimal field3;
}
- 注册这个关联模型(MyUserMappingModuleRegister)
/*** 这个组织机构-用户关联类型,所以需要进行描述类型的验证和转换* @author yinwenjie*/
@Component
public class MyUserMappingModuleRegister implements UserMappingModuleRegister<MyMappingUser> {@Overridepublic int getOrder() {return 1;}// 指定一种类型@Overridepublic String type() {return MyMappingUser.MY_MAPPING_ORG;}@Overridepublic Class<? extends MyMappingUser> getMappingClass() {return MyMappingUser.class;}@SuppressWarnings("unchecked")@Overridepublic List<MyMappingUser> transform(JSONArray users) {// 只有要求转换的用户关联信息,其type都为default的时候,才进行转换,否则报错Validate.isTrue(!CollectionUtils.isEmpty(users) , "传入的用户json集合不能为null");for (Object userItem : users) {JSONObject userObject = (JSONObject)userItem;String type = userObject.getString("type");String account = userObject.getString("account");Validate.notBlank(type , "进行转换时,用户类型必须传入");Validate.notBlank(account , "进行转换时,账户信息必须传入");// 进行特有的业务信息的验证// ......}// 开始进行转换List<MyMappingUser> userObjects = (List<MyMappingUser>)JSONArray.parseArray(users.toJSONString(), this.getMappingClass());return userObjects;}
}
5.2.3、对默认提供的组织机构数据模型进行扩展
- 扩展组织机构默认实现中提供的组织机构模型定义,加入新的项目上的特有字段(ExtendDefaultOrg)
/*** 满足第二个需求变动点,在默认的组织机构上增加“有效时间”、“经办方式”、“预算上限”三个特定字段* 这种继承方式的问题后文还会进行详细讲解*/
@Getter
@Setter
public class ExtendDefaultOrg extends DefaultOrg {// 有效时间private Date effectiveTime;// 经办方式private String handleMethod;// 预算上限private BigDecimal upperLimit;
}
- 将这个扩展的模型重新注册进行注册,并调整注册中的具体类型说明
@Component
public class ExtendsOrganizationModuleRegister extends DefaultOrganizationModuleRegister {// 二开团队扩展DefaultOrg后,涉及到类型转换的逻辑基本不会修改,只需要指定class类型即可@Overridepublic Class<? extends DefaultOrg> getOrgClass() {return ExtendDefaultOrg.class;}
}
根据经典的依赖设计原则,应该优先考虑实现接口而不是优先考虑继承。但是在实际工作中,根据特定的需求场景,技术人员是需要使用继承的。从本小节对默认的组织机构类型进行扩展的设计来看,就是使用的继承——继承原有的默认组织机构类型(DefaultOrg),并在此基础上增加特定的业务字段。
但是这种使用“继承”的方式会带来一系列问题,主要包括:
-
由于默认的组织机构类型(DefaultOrg)是有组织机构默认的本地数据库实现所提供(simple.org.local),所以一旦产生继承关系就意味着应用程序(Spring-Boot)以后都必须依赖组织机构的本地实现(simple-org-local-starter)才能正常工作。
-
由于技术人员依赖了组织机构模块默认的本地数据库实现,并且继承业务模型后中增加了特定的业务字段。那么数据层的模型和功能该怎么办?数据层的模型和功能是否需要重新书写,又或者数据层模型(DefaultOrgEntity)和功能(DefaultOrgRepository)在原有基础上进行继承呢?如果采用继承的方式进行扩展,那么数据层功能中原有的方法肯定不适用,因为他们都是返回的DefaultOrgEntity,而不是MyOrgEntity。所以本文的建议是,即使使用了继承体系,那么数据层的相关模型和功能也最好进行重新书写。
5.3、进行业务行为的定制开发
有了对模型的描述和定义后,我们需要对新模型的功能行为进行描述。这里的描述分为两个层面,第一个层面是为新的模型能够融入组织机构模块业已存在的功能,进行的行为描述。例如所有接入组织机构模块的节点,如果支持树形结构的展示那么都要描述自己如何查询关联的下级组织机构和下级用户信息。
第二个层面是这些新的模型所代表的定制化业务都有伴随着一些定制化的功能要求,例如项目团队自己要求创建的这种组织机构类型中,存在“机构级别”这样的业务字段,并且项目客户要求提供“按照机构级别”进行组织机构信息查询这样的定制化功能。
5.3.1、接入组织机构模块既有的行为
- 首先为了自定义的组织机构模型能够接入既有的功能,需要实现OrganizationStrategy接口
/*** 保证自定义的组织机构类型,能够在组织机构既定的处理逻辑协调下,实现最基本的增、删、改、查功能*/
@Component
public class MyOrgStrategy implements OrganizationStrategy<MyOrg> {// 特定的数据层服务@Autowiredprivate MyOrgRepository myOrgRepository;@Overridepublic String type() {return MyOrg.MY_ORG;}@Overridepublic Collection<MyOrg> queryChilds(String parentType, String parentCode) {if(StringUtils.isAnyBlank(parentType , parentCode)) {return null;}// 由于本类型的组织机构允许用一种组织机构类型作为父级进行关联。List<MyOrgEntity> myOrgEntities = this.myOrgRepository.findByParentCodeAndParentType(parentType, parentCode);if(CollectionUtils.isEmpty(myOrgEntities)) {return Lists.newArrayList();}// 转换为业务模型,然后进行返回List<MyOrg> myOrgs = this.transform(myOrgEntities);return myOrgs;}// 其他实现代码(新增、修改等功能)略// ......
}
- 然后是基于MyMappingUser模型实现UserMappingStrategy接口,以保证新的组织机构-用户关联信息,能够接入组织机构模块已有的控制逻辑。
/*** 保证自定义的组织机构-用户关联信息,能够在组织机构模块既定的处理逻辑协调下,* 实现最基本的增、删、改、查功能*/
@Component
public class MyMappingUserStrategy implements UserMappingStrategy<MyMappingUser> {@Autowiredprivate MyMappingUserRepository myMappingUserRepository;@Overridepublic int getOrder() {return 2;}@Overridepublic String type() {return MyMappingUser.MY_MAPPING_ORG;}@Overridepublic Collection<MyMappingUser> queryUsers(String parentType, String parentCode) {if(StringUtils.isAnyBlank(parentType , parentCode)) {return Lists.newArrayList();}// 进行数据层的查询List<MyMappingUserEntity> mappingUserEntities = this.myMappingUserRepository.findByParentCodeAndParentType(parentCode, parentType);if(CollectionUtils.isEmpty(mappingUserEntities)) {return Lists.newArrayList();}// 然后进行转换和返回return this.transform(mappingUserEntities);}// 其它方法略// ......
}
5.3.2、定制化功能的开发
从需求上来看,项目上的开发人员还需要为新定制的组织机构模型,增加一个特定的查询方法。例如上文提到的,按照特定的机构级别信息进行查询。
- 这是定制的服务层接口:MyOrgService
/*** 由于需求中明确,本项目中特定业务模型还有这个特定业务模型所需要的新的查询功能,* 那么可以根据这个特定行为要求,自行创建服务层接口* @author yinwenjie*/
public interface MyOrgService {/*** 按照特定的机构级别信息,进行查询* @param level* @return*/public List<MyOrg> findByLevel(String level);
}
- 以下是接口实现:MyOrgServiceImpl
@Service
public class MyOrgServiceImpl implements MyOrgService {@Autowiredprivate MyOrgRepository myOrgRepository;@Overridepublic List<MyOrg> findByLevel(String level) {if(StringUtils.isBlank(level)) {return Lists.newArrayList();}List<MyOrgEntity> myOrgEntities = this.myOrgRepository.findByLevel(level);if(CollectionUtils.isEmpty(myOrgEntities)) {return Lists.newArrayList();}// 转换成业务模型后,进行返回return this.transform(myOrgEntities);}// 非必要代码省略// ......
}
注意,从以上示例代码中我们至少看到了以下几个现象:
-
和自定义组织机构类型相关的特异性功能,统一放在名为MyOrgService的服务层接口定义中,且这个接口定义直接放置在Spring-Boot应用程序的脚手架中。也就是说这个服务层的接口只对该应用程序有效,组织机构模块的默认实现或者其它应用程序是无法看到更无法使用这些接口功能。
-
为了便于管理,除了那些需要进行业务抽象后统一管理的业务行为,其它在项目层面自定义的业务行为都被按照业务模型进行分类定义。也就是说项目层面上自定义的组织机构模型(MyOrg)其特有的业务行为被定义在MyOrgService接口中、项目层面上自定义的组织机构-用户模型(MyMappingUser)其特有的业务行为被定义在MyMappingUserService中。
6、示例总结
上图展示了本专题第6篇、第7篇、第8篇文章描述的组织机构模块的接口定义、模块默认实现、模块二次开发支持相关的所有工程结构细节。我们来总结一下整个示例所展示出来的关键细节:
-
从需求提出到业务功能的具体技术解决方案,其过程包含一个重要的需求分析步骤。这个需求分析步骤主要是通过业务抽象的思想,分析出需求中所包括的不变部分和可变部分。可变部分又可以被分解成多个业务维度,这些业务维度都通过定义接口的方式提供出来供技术人员进行不同的落地实现。在组织机构功能的业务抽象后,我们找到了两个业务维度:组织机构类型和组织机构-用户的关联类型。
-
经过业务抽象所形成的功能模块方案,一定会有一个接口定义层。在这个组织机构模块的实例中,集中形成的接口定义层,就是名叫org-sdk的脚手架。通过观察这个脚手架,组织机构模块的接口定义层除了最多可能包括一个默认的控制逻辑(具体的Class类)外,确实只有接口定义存在。包括业务模型的结构也只存在接口定义(如Organization接口、UserMapping接口)。
-
这个接口定义层是降低模块耦合度的重要基础。通过这个接口定义层,可以控制不同的业务维度的具体实现最终组合成不同的业务逻辑线;通过这个接口定义层,可以稳定功能模块在应用系统中的技术分层,控制循环依赖;通过这个接口定义层,可以隔离需求变化导致的变更涟漪效应,保证需求变化只会使某种具体的业务逻辑在一定功能边界内受到影响。
-
优先考虑实现而非继承是有现实意义的,例如本示例中由于需要扩展组织机构默认实现中的默认组织机构类型(在其中加入两个项目上特有的字段),所以ExtendDefaultOrg就继承了DefaultOrg。就这一个小小的设计,使整个应用程序付出了较高的维护成本,后文我们将介绍出现这种情况的优化思路,但都不能彻底将维护成本降低到原来的样子。
-
在这个组织机构模块的示例讲解过程中,我们涉及到了几个设计名词:业务抽象、业务屈服度、业务维度、相交的业务维度和不相交的业务维度。本专题的后续文章中,会深入介绍业务抽象的理论细节,并会对这些设计名词进行详细的阐述。
-
最后完整的示例代码可通过该处链接进行完整下载:此链接可以进行下载(https://download.csdn.net/download/yinwenjie/88617268?spm=1001.2014.3001.5501)。如各位读者在阅读完这三篇示例文章后,还是对“为什么要这样做”有疑问,可以将问题发在评论区。实际上对于如何通过业务抽象的思想降低模块间耦合强度,以及其中包含的设计优势,还需要在更深入了解业务抽象的理论后,才会有更深入的设计体会。后续文章我们将开始对设计理论进行逐个剖析。下图展示了一个根据业务抽象的设计理论完成的低耦合模块的应用程序分层依赖的效果图。
如果读者观察上图时心中仍然还有**“如果访问权限模块需要获取角色信息,但访问权限模块又不允许依赖角色模块,那怎么调用角色模块的功能获取角色信息?”**这样的疑问,那确实需要继续阅读本专题后续的文章。后文本专题将展开更理论化的讲解。
相关文章:

软件设计不是CRUD(8):低耦合模块设计实战——组织机构模块(下)
接上文《软件设计不是CRUD(7):低耦合模块设计实战——组织机构模块(中)》 5、某项目研发团队进行扩展 上文中我们介绍了如何研发一个具有较低耦合强度的组织机构模块(包括模块的SDK和模块的默认本地数据库…...

docker-compose Install gitea
gitea 前言 Gitea 是一个轻量级的 DevOps 平台软件。从开发计划到产品成型的整个软件生命周期,他都能够高效而轻松的帮助团队和开发者。包括 Git 托管、代码审查、团队协作、软件包注册和 CI/CD。它与 GitHub、Bitbucket 和 GitLab 等比较类似。 Gitea 最初是从 Gogs 分支而来…...

【Pytorch】学习记录分享1——Tensor张量初始化与基本操作
1. 基础资料汇总 资料汇总 pytroch中文版本教程 PyTorch入门教程 B站强推!2023公认最通俗易懂的【PyTorch】教程,200集付费课程(附代码)人工智能_机器 视频 1.PyTorch简介 2.PyTorch环境搭建 basic: python numpy pandas pytroch…...

Python数据科学视频讲解:Python的数据运算符
2.9 Python的数据运算符 视频为《Python数据科学应用从入门到精通》张甜 杨维忠 清华大学出版社一书的随书赠送视频讲解2.9节内容。本书已正式出版上市,当当、京东、淘宝等平台热销中,搜索书名即可。内容涵盖数据科学应用的全流程,包括数据科…...

参数学习——糖果问题(人工智能期末复习)
之前看了好久都不知道这题咋写,后来看了这篇机器智能-高频问题:糖果问题,大概看明白了,其实主要围绕着这两个公式 光看公式也看不懂,还是要结合题目来 己知有草莓味和酸橙味两种类型的糖果,分别放入5种不同…...

【深度学习】注意力机制(六)
本文介绍一些注意力机制的实现,包括MobileVITv1/MobileVITv2/DAT/CrossFormer/MOA。 【深度学习】注意力机制(一) 【深度学习】注意力机制(二) 【深度学习】注意力机制(三) 【深度学习】注意…...

螺旋矩阵算法(leetcode第59题)
题目描述: 给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。示例 1:输入:n 3 输出:[[1,2,3],[8,9,4],[7,6,5]] 示例 2:输入&#…...

SQL Server 服务启动报错:错误1069:由于登录失败而无法启动服务
现象 服务器异常关机以后,SQL Server服务无法启动了。 启动服务时报错: 错误1069:由于登录失败而无法启动服务 解决办法 我的电脑–控制面板–管理工具–服务–右键MSSQLSERVER–属性–登录–登陆身份–选择"本地系统帐户" 设置完成后&am…...

“ABCD“[(int)qrand() % 4]作用
ABCD[(int)qrand() % 4] 作用 具体来说: qrand() 是一个函数,通常在C中用于生成一个随机整数。% 4 会取 qrand() 生成的随机数除以4的余数。因为4只有四个不同的余数(0, 1, 2, 3),所以这实际上会生成一个0到3之间的随…...

Vue2面试题:说一下组件通信有哪些方式?
父传子 1、自定义属性 props:在父组件中,给子组件绑定一个自定义属性,在子组件中,通过props进行接收 2、$parent:直接访问父组件实例的属性和方法 3、$attrs:在父组件中,给子组件绑定一个自定义…...

C# 两个日期比较大小
文章目录 C# 两个日期比较大小直接比较大小工具类DateTime.Compare C# 两个日期比较大小 直接比较大小 string ed "2023-12-13 09:27:59.000";//过去式DateTime nowDateTime DateTime.Now;DateTime expirationDate Convert.ToDateTime(ed);//质保期 长日期DateT…...

路由基本原理
目录 一、路由器概述 二、路由器的工作原理 三、路由表的形成 四、路由配置 1.连接设备 2.进入系统模式 3.进入接口模式 4.配置网络 5.下一跳的设置 6.设置浮动路由 7.设置默认路由 一、路由器概述 路由器(Router)是一种用于连接不同网络或子…...

配置本地端口镜像示例
镜像概念 定义 镜像是指将指定源的报文复制一份到目的端口。指定源被称为镜像源,目的端口被称为观察端口,复制的报文被称为镜像报文。 镜像可以在不影响设备对原始报文正常处理的情况下,将其复制一份,并通过观察端口发送给监控…...

使用FluentAvalonia组件库快速完成Avalonia前端开发
前言 工欲善其事必先利其器,前面我们花了几篇文章介绍了Avalonia框架以及如何在Avalonia框架下面使用PrismAvalonia完成MVV模式的开发。今天我们将介绍一款重磅级的Avalonia前端组件库,里面封装了我们开发中常用的组件,这样就不用我们自己再写组件了。专注业务功能开发,提…...

JAVA实体类集合该如何去重?
JAVA实体类集合该如何去重? 最近在工作中经常遇到需要去重的需求,所以特意系统的来梳理一下 有目录,不迷路 JAVA实体类集合该如何去重?单元素去重方法一:利用Set去重方法二:利用java 8的stream写法…...

修改Element UI可清空Input的样式
如图所示,修改Input右侧的清空按钮位置: <el-input class"create-catalog-ipt"placeholder"请输入相关章节标题"v-model"currentCatalogTitle"clearable /> // SCSS环境 ::v-deep {.create-catalog-ipt {input {he…...

Java常用注解
文章目录 第一章、Java注解与元数据1.1)元数据与注解概念介绍1.2)Java注解的作用和使用1.3)注解的分类 第二章、Mybatis框架常用注解2.1)Mybatis注解概览2.2)常用注解MapperScanMapperSelectInsertUpdateDeleteParam结…...

golang实现同步阻塞、同步非阻塞、异步非阻塞IO模型
一、同步阻塞IO模型TCP和HTTP示例 同步阻塞IO符合我们的直觉认知,应用程序从TCP连接接收数据缓冲区接受数据,如果没有数据就等待——此处就是阻塞,如果有数据需要把数据从内核空间读取到用户空间——此处就是同步。 在Go语言中进行同步阻塞IO编程TCP交互,可以使用标准库中…...

java SSM教师工作量管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计
一、源码特点 java SSM 教师工作量管理系统是一套完善的web设计系统(系统采用SSM框架进行设计开发,springspringMVCmybatis),对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要…...

大数据技术之Hive(超级详细)
第1章 Hive入门 1.1 什么是Hive Hive:由Facebook开源用于解决海量结构化日志的数据统计。 Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类SQL查询功能。 本质是:将HQL转化成MapReduce程序 …...

NVMe over Fabrics with SPDK with iRDMA总结 - 1
1.0 Introduction简介 NVM Express* (NVMe*) drives are high-speed, low-latency, solid-state drives (SSDs), that connect over the server Peripheral Component Interconnect Express* (PCIe*) bus. NVM Express* (NVMe*) 硬盘是高速、低延迟的固态硬盘 (SSD),通过服…...

【PTA刷题】求链式线性表的倒数第K项(代码+详解)
文章目录 题目代码详解 题目 给定一系列正整数,请设计一个尽可能高效的算法,查找倒数第K个位置上的数字。 输入格式: 输入首先给出一个正整数K,随后是若干非负整数,最后以一个负整数表示结尾(该负数不算在序列内&#…...

VSCode 创建工作区,多文件夹终端切换
VSCode 创建工作区的好处有以下几点: 项目结构清晰:每个工作区都有自己的文件夹结构,可以更好地组织和管理项目文件。版本控制:VSCode 支持多种版本控制系统,如Git,可以在工作区内进行代码的版本管理。插件…...

高阶函数(js的问题)
(1)函数可以作为参数被传递 (2)函数可以作为返回值输出 4-1.函数作为参数传递 Array.prototype.sort方法: var array [10,5,12,3];array.sort();//array:[10,12,3,5]//如代码那样,排序的结果并不是我们想要…...

android-android源码目录
android源码目录 Android.bp art bionic bootable bootstrap.bash build build.sh compatibility cts dalvik developers development device external frameworks: Android 系统的核心框架代码av: 该目录包含与音视频相关的框架和库,如音频解码器、视频编码器、多…...

Json格式化
Json格式化 大家好,我是微赚淘客机器人的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! Json格式化:让数据更亮眼,解密Json的奇妙世界 在现代Web开发中,Json(JavaScript Object N…...

数据可视化设计:让数据故事更有说服力
写在开头 在数字化的时代,数据如同一把锁住的宝剑,等待我们挥舞。然而,唯有通过巧妙运用数据可视化的原则和技术,我们才能真正解锁数据的力量,创造出令人信服的数据故事。本文将深入研究数据可视化设计的奥秘,揭示其中的魔法,让你在数据的海洋中游刃有余,用数据的语言…...

java面试题-Spring事务以及@Transactional注解详解
远离八股文,面试大白话,通俗且易懂 看完后试着用自己的话复述出来。有问题请指出,有需要帮助理解的或者遇到的真实面试题不知道怎么总结的也请评论中写出来,大家一起解决。 java面试题汇总-目录-持续更新中 对于这个面试中高频问到…...

ARM流水灯
.text .global _start _start: LED1 1.RCC时钟使能GPIOE RCC_MP_AHB4ENSETR[4]->1 LDR R0,0x50000a28 LDR R1,[R0] ORR R1,R1,#(0x1<<4) STR R1,[R0] 2.设置PE10为输出模式 GPIOE_MODER[21:20]->01 先清0 LDR R0,0x50006000 LDR R1,[R0] BIC R1,R1,#(0x3<&…...

docker-compose单机容器编排
Dockerfile:先配置好文件,然后build,镜像-------->容器。 docker-conpose 既可以基于dockerfile,也可以基于镜像,一键式拉起镜像和容器。 docker-compose核心就是yml文件,可以定义容器的一切。通过yml配置,直接运行…...