关于DDD和COLA的一些总结和思考

1|0思维:面向对象和面向过程

领域驱动设计本质上是讲的面向对象,但是谈面向对象,始终无法绕开面向过程,所以我们先好好说一下面向过程和面向对象这两个概念。

什么是面向过程呢,其实就是我们学习编程时最初被植入的逻辑,这也是很多人即便学了面向对象后,写的代码却四不像的原因,因为这个思维是根深蒂固的。我想大多数院校科班生,第一次接触编程都是C语言,从一个hello word开始,然后便是if elsefor循环,其实if else 思维便是面向过程的基本逻辑,就是做事的 “步骤”,比如经典的图书管理系统的课设,基于面向过程去设计编码,想的是新增图书、修改图书名字、描述;是根据输入的参数,进行if else的选择,然后进入对应的流程,在流程里去先做什么,再做什么,关注点在图书的操作上, 是专注于事情本身,专注于做事的流程;所以面向过程更适合去做一些底层的,基于硬逻辑的内容。当需要做的东西规模过大且频繁变化时,代码量和改动成本也会增加。

面向对象相对于面向过程,它更偏向于概念的抽象和建设模型。同样图书管理系统,面向对象考虑的重点则变成了图书(而不是操作图书数据这件事),一条条的图书数据,在内存里就是一个个的个体对象,至于图书的各种操作那是细节的内容,它不会去关心,只要定义了图书这个对象,那对于图书的操作那都在对象自身的事情,面向对象专注于事情的主体,是以主体以及它们之间的行为组合去构建程序。当然对象自身内部的行为又是一个个的面向过程组成,这就是在编码的时候最容易让人模糊和把握不准的地方。面向对象把程序设计又拔高了一层,把细节忽略,站在更高维度去构建程序。

通过一个简单的例子来对比一下这两种思想:数据库中存在所有学生的数据,比如姓名、学校、专业,下面需要实现一个自我介绍的功能,描述方式为:我是XX,毕业于XXX学校,用面向过程的思维实现是这样的:

public class test{public static void main(String[] args){desc("张三");}public static void desc(String name){//查询数据库connection = DbManager.createConnection(root,XXX,3306);//查询数据Map<String,Object> a = connection.query("SELECT name,school FROM tb_student WHERE name = #{name}",name);System.out.print("我是"+a.get('name')+",毕业于"+a.get("school");}
}

拿到需求,我们关注的自我介绍这件事,只要完成这件事就好了,所以直接定义一个过程(方法、函数)然后过程里去根据需求把这件事完成;

而使用面向对象的话,面对需求,首先需要确定主体,也就是学生对象,然后学生对象有姓名、学校、专业这些属性和一个自我描述的能力。

public class Student{private String name;private String school;private String disciplinepublic Student(Map map){//省略构造函数内容}public void introduce(){System.out.print("我是"+this.name+",毕业于"+this.school);
}
}

然后定义另一个对象,数据库对象,数据库对象有一个可以查询学生对象的能力

public class Db{private String url;private String username;//其他属性…… public Student searchStudent(String name){Map a = connection.query("SELECT name,school FROM tb_student WHERE name = #{name}",name);  return new Student(a);}
}

最后通过使用两个对象,来完成这件事

public class test{public static void main(String[] args){Student object = Db.searchStudent("张三")object.introduce();}
}

通过上面的代码,可以发现,面向对象的实现似乎需要更多的代码来完成这件事,没错,这是事实,虽然在设计上我们忽略细节,可是编码上是无法忽略的,甚至使用面向对象成本更高,但是注意我这里说的是针对咱们这个场景需求,当前场景如果戛然而止,确实面向过程方式更精简,但是如果需求继续增加,随着业务增加、需求变大、边界变宽,面向过程可能就需要追加更多的过程代码去完成,而面向对象可能需要的是调整对象的组合方式或者对象本身的扩展去完成,所以,面向对象在代码层面最大的优势就是 复用和扩展

2|0业务开发面向对象理论:领域驱动

说完面向过程和面向对象,再说一下关于面向对象的集成方法论—领域驱动,它的本质是统一语言、边界划分和面向对象分析的方法。简单点来讲就是将OOA、OOD和OOP融汇贯通到系统开发中,充分发挥面向对象的优势和特点,去降低系统开发过程中的熵增。狭义一点解释就是如何用 java 在业务开发中写出“真正面向对象”的系统代码。

在概念上,领域驱动又分为贫血模式和充血模式。

2|1贫血模式

贫血模式很多人不陌生, 也是大多数Java开发使用的MVC架构,实体类仅有get和set方法,在整个系统中,领域对象几乎只作传输介质的作用,不会影响到层次的划分,业务逻辑多集中在Service中,也就是绝大多数使用Spring框架进行开发的Web项目的标准形态,即Controller、Service、Dao、POJO;

这里看一个例子:

/*** 账户业务对象*/
public class AccountBO {/*** 账户ID*/private String accountId;/*** 账户余额*/private Long balance;/*** 是否冻结*/private boolean isFrozen;public String getAccountId() {return accountId;}public void setAccountId(String accountId) {this.accountId = accountId;}public Long getBalance() {return balance;}public void setBalance(Long balance) {this.balance = balance;}public boolean isFrozen() {return isFrozen;}public void setFrozen(boolean isFrozen) {this.isFrozen = isFrozen;}}/*** 转账业务服务实现*/
@Service
public class TransferServiceImpl implements TransferService {@Autowiredprivate AccountMapper accountMapper;@Overridepublic boolean transfer(String fromAccountId, String toAccountId, Long amount) {AccountBO fromAccount = accountMapper.getAccountById(fromAccountId);AccountBO toAccount = accountMapper.getAccountById(toAccountId);/** 检查转出账户 **/if (fromAccount.isFrozen()) {throw new MyBizException(ErrorCodeBiz.ACCOUNT_FROZEN);}if (fromAccount.getBalance() < amount) {throw new MyBizException(ErrorCodeBiz.INSUFFICIENT_BALANCE);}fromAccount.setBalance(fromAccount.getBalance() - amount);/** 检查转入账户 **/if (toAccount.isFrozen()) {throw new MyBizException(ErrorCodeBiz.ACCOUNT_FROZEN);}toAccount.setBalance(toAccount.getBalance() + amount);/** 更新数据库 **/accountMapper.updateAccount(fromAccount);accountMapper.updateAccount(toAccount);return Boolean.TRUE;}
}

贫血模型的问题和难点在于,在面对较为庞大体量的业务系统时,业务逻辑层的膨胀导致代码的混乱。因为贫血的特性(POJO仅仅是数据),导致业务代码本身就不“面向对象”化,随着业务的积累和缝缝补补,Service层更像是面向过程,堆满了if else ,会不断膨胀和混乱,边界不易控制,内部的各模块、包之间的依赖会变得不易管理。

2|2充血模式

充血模式更符合领域设计,简单来讲就是OOA和OOP的最佳实践,将业务逻辑和持久化等内容均内聚到实体类中,Service层仅仅充当组合实体对象的画布,负责简单封装部分业务和事务权限管理等;

如果使用充血模式完成上面的例子则是这样:

/*** 账户业务对象*/
public class AccountBO {/*** 账户ID*/private String accountId;/*** 账户余额*/private Long balance;/*** 是否冻结*/private boolean isFrozen;/*** 出借策略*/private DebitPolicy debitPolicy;/*** 入账策略*/private CreditPolicy creditPolicy;/*** 出借方法* * @param amount 金额*/public void debit(Long amount) {debitPolicy.preDebit(this, amount);this.balance -= amount;debitPolicy.afterDebit(this, amount);}/*** 转入方法* * @param amount 金额*/public void credit(Long amount) {creditPolicy.preCredit(this, amount);this.balance += amount;creditPolicy.afterCredit(this, amount);}public boolean isFrozen() {return isFrozen;}public void setFrozen(boolean isFrozen) {this.isFrozen = isFrozen;}public String getAccountId() {return accountId;}public void setAccountId(String accountId) {this.accountId = accountId;}public Long getBalance() {return balance;}/*** BO和DO转换必须加set方法这是一种权衡*/public void setBalance(Long balance) {this.balance = balance;}public DebitPolicy getDebitPolicy() {return debitPolicy;}public void setDebitPolicy(DebitPolicy debitPolicy) {this.debitPolicy = debitPolicy;}public CreditPolicy getCreditPolicy() {return creditPolicy;}public void setCreditPolicy(CreditPolicy creditPolicy) {this.creditPolicy = creditPolicy;}
}/*** 入账策略实现*/
@Service
public class CreditPolicyImpl implements CreditPolicy {@Overridepublic void preCredit(AccountBO account, Long amount) {if (account.isFrozen()) {throw new MyBizException(ErrorCodeBiz.ACCOUNT_FROZEN);}        }@Overridepublic void afterCredit(AccountBO account, Long amount) {System.out.println("afterCredit");}
}/*** 出借策略实现*/
@Service
public class DebitPolicyImpl implements DebitPolicy {@Overridepublic void preDebit(AccountBO account, Long amount) {if (account.isFrozen()) {throw new MyBizException(ErrorCodeBiz.ACCOUNT_FROZEN);}if (account.getBalance() < amount) {throw new MyBizException(ErrorCodeBiz.INSUFFICIENT_BALANCE);}}@Overridepublic void afterDebit(AccountBO account, Long amount) {System.out.println("afterDebit");}
}/*** 转账业务服务实现*/
@Service
public class TransferServiceImpl implements TransferService {@Resourceprivate AccountMapper accountMapper;@Resourceprivate CreditPolicy creditPolicy;@Resourceprivate DebitPolicy debitPolicy;@Overridepublic boolean transfer(String fromAccountId, String toAccountId, Long amount) {AccountBO fromAccount = accountMapper.getAccountById(fromAccountId);AccountBO toAccount = accountMapper.getAccountById(toAccountId);//此处采用轻量化地方式解决了自身面向对象和Spring的bean管理权矛盾fromAccount.setDebitPolicy(debitPolicy);toAccount.setCreditPolicy(creditPolicy);fromAccount.debit(amount);toAccount.credit(amount);accountMapper.updateAccount(fromAccount);accountMapper.updateAccount(toAccount);return Boolean.TRUE;}
}

充血模型的问题和难点在于:

  • Spring框架本身的限制

  • 业务复杂程度是否匹配

  • 如何把握domain层的边界以及各层间的关系

先说Spring框架本身的限制问题,使用Spring其实默认了使用贫血模型,这是由Spring本身框架特点决定的,首先Spring家族作为Java系的行业老大哥框架,积累和沉淀非常丰富,各方面已经封装的很全面,所以留出的“自由度”就比较少,它的宗旨就是让开发人员减少重复造轮子,仅仅专注功能的开发就好,所以Spring官方的demo以及一些使用导向都是贫血模式;例如Spring的根基Bean管理机制,把对象的管控牢牢把握在框架中,这种将实体类的管理也交由Spring本身也降低了二次开发时面向对象的属性,导致在Spring中进行bean之间的引用改造会面临大范围的bean嵌套构造器的调用问题。

其次是业务的复杂程度是否适配,绝大多数的项目,说难听点,都是面向数据的概念意淫、CRUD的“建筑行业工地式”项目而已,使用Spring+贫血的经典模式足够满足,而且贫血模式在开发成本上更适合面向数据开发(需求变更的驱动成因、团队的管理方式、研发团队的素质),如果过分追求面向对象反而有些舍近求远,所以能否根据业务场景决定是否使用充血的领域驱动是挺难的(毕竟很多优秀的面向对象研发是很难舍弃面向对象的诱惑)

最后就是最难的,如何划分业务逻辑到domain层,即什么样的逻辑应该放在Domain Object中,什么样的业务逻辑应该放在Service中,这是很含糊的,如果没有面向领域开发流程以 OO思想的充分沉淀积累,很难做到;即使划分好了业务逻辑,由于分散在Service和DomainObject层中,不能更好的分模块开发。熟悉业务逻辑的开发人员需要渗透到Domain中去,而在Domian层又包含了持久化,对于开发者(习惯于贫血模型的团队)来说这十分混乱。

3|0关于COLA框架

COLA 是 Clean Object-Oriented and Layered Architecture的缩写,代表“整洁面向对象分层架构”。由阿里大佬张建飞所提出的一种基于DDD和代码整洁理论所诞生的实践理论框架,详细内容可阅读《程序员的底层思维》和相关git代码去了解

项目地址:GitHub - alibaba/COLA: 🥤 COLA: Clean Object-oriented & Layered Architecture

下面简单把COLA框架的主要架构梳理一下,这里的框架图采用COLA4.0版本(当前作者认为的最优形态)

COLA整体上由两部分组成,代码架构和通用组件,两者并非强绑定,代码架构是一套基于maven的代码模板,通用组件则是可选项,可以理解为Common组件包。

3|1Adapter层(适配层)

负责对前端展示(Web、Wireless、WAP)的路由和适配。对于传统B/S系统而言,Adapter层就相当于MVC中的Controller。

3|2App层(应用层)

主要负责获取输入、组装上下文、参数校验、调用领域层做业务处理,以及发送消息通知(如有需要)等。层次是开放的,应用层也可以绕过领域层,直接访问基础设施层。

3|3Domain层(领域层)

用于封装核心业务逻辑,并利用领域服务(Domain Service)和领域对象(Domain Entity)的方法对App层提供业务实体和业务逻辑计算。领域层是应用的核心,不依赖任何其他层次。

3|4Infrastructure层(基础设施层)

主要负责技术细节问题的处理,比如数据库的CRUD、搜索引擎、文件系统、分布式服务的RPC等。此外,Infrastructure层还肩负着领域防腐的重任,外部依赖需要通过Gateway的转义处理,才能被App层和Domain层使用。

3|5Commpont(外挂Cola扩展组件)

非业务相关的功能组件包

3|6对象类型概念统一

使用OO理论及COLA,首先就是要明确各类对象的概念,统一认知并且严格执行。

DO(Data Object):数据对象

DO应该跟其名字一样,仅作为数据库中表的1:1映射,不参与到业务逻辑操作中,仅仅负责Infrastructure层的数据持久化以及读取的实体对象

Entity:实体对象

对应业务模型中的业务对象,字段及方法应该与业务语言抱持一致,原则上Entity和Do应该是包含嵌套的关系,且Entity的生命周期仅存在在系统的内存中,不需要序列化以及持久化,业务流程结束,Entity也应该跟随消亡

DTO(Data Transfer Object): 数据传输对象

主要作为和系统外部交互的对象,DTO主要是数据传输的载体,期间可以承载一部分领域能力(业务相关的判定职责等);

VO(view Object):展示层对象

性质和DTO相差不多,也是数据传输载体,主要是作为对外交付数据的载体,承载部分数据隐藏、数据保护、数据结构化展示的作用。

3|7代码组织结构及层次间关系

使用作者推荐的maven Archetypes模板后,能发现COLA整体采用maven module的结构来构建代码,划分原则是结合技术维度和领域维度综合划分:

相应的分层则对应到不同的module中(技术维度划分),每一层对应一个maven的module

而每个module(层)之间的依赖关系是这样的:

Comm本身作为公共内容被所有module依赖,Adapter作为系统唯一外部输出仅依赖APP,App本身除了依赖领域层外还因为CQRS相关内容依赖Infras;Domain层巧妙地使用依赖倒置让Infras层反向依赖它,以保证了Domain的独立和完整。整体是按照技术维度进行划分的。

然后就是关于每个module的代码目录划分原则(基于领域维度进行划分),即每个module中对应的概念都划分到具体某一功能领域包中:

下面是一个单体COLA代码框架样例:

<modules><module>eden-demo-cola-adapter</module><module>eden-demo-cola-app</module><module>eden-demo-cola-domain</module><module>eden-demo-cola-infrastructure</module><module>start</module>
</modules>

像上文中说的,整体采用Maven的module,每一层对应一个module。

具体的adapter等层中,由参照领域划分原则进行包的划分,而Domian层中的gateway仅仅定义interface,而在infrast层中进行gateway的Impl类,由此完成了巧妙地依赖倒置,解耦了Domain,让Domain地module作为一个内聚的个体,聚焦核心业务开发。

3|8框架运行逻辑

场景1(包含业务诉求)

由客户端发起请求,首先Adapter层接收请求,进行相关的校验后进行数据封装(Coverter操作:由请求参数封装为App层所需要的DTO结构),根据预设的逻辑去调用App的内容;DTO数据进入App后, App根据业务诉求,然后调用Domain的ability和module进行业务组装(Converter操作:将DTO转换为Entity对象);被调用的Domian在进行业务处理过程中调用Infras层去进行相关持久化和查询;Infras在被Domain调用时,需要把传入的Entity转换为内部对应数据的DO对象。最后逐层返回,相应的在Adapter层将DTO转为VO

场景2(CQRS诉求):

由外部客户端发起的不携带业务处理的查询操作,例如:某某数据列表查看、某某内容统计,则由Adapter接收请求后,封装参数对象后,调用相关App内容,App内部进行DTO和DO的相互coverter操作后,直接调用Infras层进行数据的查询操作。

从上面的运行流程不难看出,COLA在代码上有两个特点

  1. Domain层足够独立且直至业务核心,通过巧妙的依赖倒置(gateway的接口与实现切割至两个module中),完成了与基础数据持久层的耦合,变成了最基础的被依赖者,内部分的model和ability这两部分是平等的, model是针对业务场景进行抽象的业务对象Entity、ability是抽象的业务直接的操作,相当于上文充血模式例子中AccountBO类CreditPolicyDebitPolicy的关系;优势就是可以专注于复杂的业务开发,并且保证业务代码的整洁性和可重构性。

  2. 制定了明确地规范,将部分非业务内容(数据转换、校验、适配等)均摊至不同的层次里,降低了原本贫血模式中Service持续积累过程中不可避免地臃肿;但是另一方面,为了实现面向对象与解耦,不可避免地追加了许多转换操作,存在DTO和Conver操作代码膨胀的风险

4|0写在最后

COLA框架相较于传统MVC(贫血模式)的三层结构要复杂一些,而复杂出来的内容(convertor、executor、extension、domainservice)的根本目的是在复杂的业务场景下,去践行面向对象的设计和编码,充分发挥面向对象的优势(保证代码的整洁、可维护等);但是同时也是要付出DTO对象和数据转换代码存在冗余激增风险的代价。

所以考虑使用COLA之前先谨慎考虑一下自己项目的特点,实际情况却是绝大多数业务并没有很复杂,都是基于数据的CRUD,且团队整体采用面向数据的开发方式去开发,没必要 “为了引入而引入”;其次使用COLA框架去开发是有技术成本的,至少对于团队研发工程师的要求较高(必须精通OOA、OOD、OOP),保证每个功能和需求的设计到落地都是连贯且完整的面向对象设计。

下面是针对COLA框架设计时依据的一些思想的提炼:

  1. 规范的代码框架:井井有条的包和目录分类以及统一的命名规范会像代码地图一样形成无形的约束

  2. 核心业务逻辑和技术细节分离:区分业务部分和非业务部分,并进行分离,让“上帝归上帝,凯撒归凯撒”

  3. 奥卡姆剃刀:让事情简单化,如无必须,勿增实体

  4. 合理使用领域对象模型设计domain和面向对象设计:让代码更加“面向对象”(个人比较喜欢,代码满足面向对象的一些设计原则),从而充分发挥面向对象的高扩展,易维护,实现高内聚、低耦合的究极目的。

文章转载自:Deep Blue

原文链接:https://www.cnblogs.com/TheGCC/p/18184043

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

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

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

相关文章

【安全每日一讲】加强数据安全保护 共享数字化时代便利

前言 数据安全是数据治理的核心内容之一&#xff0c;随着数据治理的深入&#xff0c;我不断的碰到数据安全中的金发姑娘问题&#xff08;指安全和效率的平衡&#xff09;。 DAMA说&#xff0c;降低风险和促进业务增长是数据安全活动的主要驱动因素&#xff0c;数据安全是一种资…

sbt安装

一、sbt介绍 在Spark中&#xff0c;sbt&#xff08;Scala Build Tool&#xff09;是一个用于构建Scala项目的工具。它是Spark项目的主要构建工具之一&#xff0c;用于编译Scala代码、管理依赖项、打包应用程序以及执行其他与项目构建相关的任务。 sbt的用途在Spark开发中主要…

基于Nios软件实现流水灯+串口输出

基于NIOS-II软核实现流水灯串口输出 引言&#xff1a; ​ 在现代电子设计领域&#xff0c;FPGA&#xff08;现场可编程门阵列&#xff09;因其灵活性和并行处理能力而成为实现复杂数字系统的首选平台。Nios II&#xff0c;作为Altera&#xff08;现为Intel旗下&#xff09;提供…

VMware虚拟机故障:“显示指定的文件不是虚拟磁盘“,处理办法

一、故障现象 由于虚拟机宕机&#xff0c;强制重新启动虚拟机后显示错误&#xff0c;没有办法启动虚拟机。 虚拟机有快照&#xff0c;执行快照还原&#xff0c;结果也不行&#xff0c;反复操作&#xff0c;在虚拟机文件目录出现很多莫名文件 二、故障原因 根据故障提示&#…

数据结构(C):玩转链表

目录 &#x1f37a;0.前言 1.链表的概念 2.链表的分类 2.1带头不带头 2.2单向和双向 2.3循环和不循环 2.4主要使用的链表 3.链表的实现 3.1申请一个链表 3.2头插和尾插 3.2.1函数的形参问题 3.2.2二级指针问题解决 3.3头删和尾删 3.4打印链表 3.5查找 3.5销…

【谷粒商城】03创建商品模块

1.创建模块 2.创建项目微服务 商品服务、仓储服务、订单服务、优惠券服务、用户服务 共同&#xff1a; 1&#xff09;、web、openfeign 2&#xff09;、每一个服务&#xff0c;包名 com.atguigu.gulimall.xxx(product/order/ware/coupon/member) 3&#xff09;、模块名&#x…

​《MATLAB科研绘图与学术图表绘制从入门到精通》示例:绘制德国每日风能和太阳能产量3D线图

在MATLAB中&#xff0c;要绘制3D线图&#xff0c;可以使用 plot3 函数。 在《MATLAB科研绘图与学术图表绘制从入门到精通》书中通过绘制德国每日风能和太阳能产量3D线图解释了如何在MATLAB中绘制3D线图。 购书地址&#xff1a;https://item.jd.com/14102657.html

完美解决Windows10下-更换JDK环境变量后,在cmd下执行仍java -version然出现原来版本的JDK的问题

一、错误场景预演 本人欲将 JDK 1.8 通过安装包的方式升级为 JDK 22。 本地旧版本&#xff1a;1.8.0_221预升级版本&#xff1a;22.0.1 1.1、查看本地旧版本 在配置环境变量之前&#xff0c;首先我们要明确&#xff0c;本地存在旧版本&#xff0c;如果本地没有 Java&#x…

MFC通过继承现有控件自定义控件

在MFC 自定义控件&#xff0c;可以通过继承MFC提供的控件类&#xff08;如CButton、CEdit、CListBox等&#xff09;并重写其成员函数和消息处理函数来实现。 以下是一个基本的步骤指南&#xff0c;用于在MFC中创建自定义控件&#xff1a; 确定要继承的基类&#xff1a; 首先…

vm16安装最新版本的ubuntu虚拟机,并安装g++的步骤记录

背景 低版本的ubuntu安装G一直不成功&#xff0c;干脆安装最新版的 官网下载 bing搜索ubuntu 下载完成 vm16新建虚拟机 一直下一步&#xff0c;安装完成 终端输入命令 sudo apt-get update ᅟᅠ       sudo apt install gcc ᅟᅠ      sudo apt install g

树莓派点亮FPGA小灯

树莓派点亮FPGA小灯 引言&#xff1a; ​ 本次实验的目的是通过树莓派和FPGA之间的串口通信&#xff0c;控制FPGA开发板上的小灯。实验将展示如何使用树莓派发送特定的字符信号&#xff0c;通过串口传输至FPGA&#xff0c;并在FPGA上实现逻辑解析&#xff0c;以点亮指定的小灯。…

【QT】QT背景介绍

本专栏内容为&#xff1a;QT学习专栏 通过本专栏的深入学习&#xff0c;你可以了解并掌握QT。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;QT &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &#x1f339;&#x1f…

3D分子生成的定制扩散框架 MolDiff - 评测

MolDiff模型是一种考虑分子键生成的3D分子生成的新模型。MolDiff是清华大学智能产业研究院马剑竹课题组发表在PMLR 2023的工作&#xff0c;第一作者是Xingang Peng&#xff0c;文章题目为&#xff1a;《 Addressing the Atom-Bond Inconsistency Problem in 3D Molecule Genera…

【Android】Kotlin学习之数据容器 -- 集合

一. 定义 List : 是一个有序列表, 可通过下标访问元素. 元素可以在list中出现多次, 元素可重复 Set : 是元素唯一的集合, 一般来说Set中元素的顺序并不重要, 无序集合. Map : 是一组键值对, 键是唯一的, 每个键刚好映射到一个值, 值可以重复 二. 集合创建 三. 示例 mutabl…

OSTE-Web-Log-Analyzer:基于Python的Web服务器日志自动化分析工具

关于OSTE-Web-Log-Analyzer OSTE-Web-Log-Analyzer是一款功能强大的Web服务器日志自动化分析工具&#xff0c;该工具专为安全研究人员设计&#xff0c;能够使用Python Web日志分析工具&#xff08;Python Web Log Analyzer&#xff09;帮助广大研究人员以自动化的形式实现Web服…

推导 模型矩阵的逆转置矩阵求运动物体的法向量

一个物体表面的法向量如何随着物体的坐标变换而改变&#xff0c;取决于变换的类型。使用逆转置矩阵&#xff0c;可以安全地解决该问题&#xff0c;而无须陷入过度复杂的计算中。 法向量变化规律 平移变换不会改变法向量&#xff0c;因为平移不会改变物体的方向。 旋转变换会改…

栈和队列的相互实现

1. 两个队列实现栈. - 力扣&#xff08;LeetCode&#xff09; 队列的特点是先进先出&#xff0c;而栈的特点是后进先出&#xff08;先进后出&#xff09;&#xff0c;也就是说重点在于利用两个队列来改变“出”的顺序。 假设我们在进行入栈操作的时候将数据依次入到一个队列中…

python如何单步调试

Python怎么单步调试&#xff1f;下面给大家介绍一下单步调试&#xff1a; 方法一&#xff1a;执行 python -m pdb myscript.py (Pdb) 会自己主动停在第一行。等待调试&#xff0c;这时你能够看看帮助。 方法二&#xff1a;在所调试程序的开头中&#xff1a;import pdb 并在你…

民航电子数据库:数据库的备份与恢复

目录 前言备份库级逻辑备份示例 恢复库级的逻辑恢复示例 前言 民航电子数据库的备份与恢复 备份 库级逻辑备份 备份目标库下所有的对象 。 因此 &#xff0c;库级逻辑备份需要由备份库的管理员&#xff08;SYSDBA&#xff09;登录至备份目标库进行操作。 语法格式 &#xff1…

商家转账到零钱怎么开通?一步步教你玩转微信营销新利器

在数字化营销日新月异的今天&#xff0c;微信支付凭借其便捷、安全的特点&#xff0c;成为了商家不可或缺的支付工具。而其中的“商家转账到零钱”功能&#xff0c;更是为商家提供了一个全新的营销利器。今天&#xff0c;我们就来详细解读一下如何开通这一功能&#xff08;我处…