【DDD】学习笔记-应用服务

Eric Evans 为运用领域驱动设计的系统架构划定了层次,在领域层和展现层之间引入了应用层(Application Layer):“应用层要尽量简单,不包含业务规则或者知识,而只为下一层(指领域层)中的领域对象协调任务,分配工作,使它们互相协作。”我在讲解领域驱动架构的演进时,则认为领域层提供了细粒度的领域模型对象,不利于它的客户端调用。因此,“基于 KISS(Keep It Simple and Stupid)原则或最小知识原则,我们希望调用者了解的知识越少越好,调用变得越简单越好,这就需要引入一个间接的层来封装。这就是应用层存在的主要意义。”

应用服务的本质

应用服务是外观模式(Facade Pattern)的体现。经典著作《设计模式》定义了外观模式的意图:“为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。”这恰与引入应用服务的作用不谋而合。使用外观模式的场景主要包括:

  • 当你要为一个复杂子系统提供一个简单接口时
  • 客户程序与抽象类的实现部分之间存在着很大的依赖性
  • 当你需要构建一个层次结构的子系统时,使用外观模式定义子系统中每层的入口点

这三个场景恰好说明了应用服务的本质。对外,应用服务为外部调用者提供了一个简单统一的接口,该接口为一个完整的用例场景提供了自给自足的功能,使得调用者无需求助于别的接口就能满足业务需求。对内,应用服务自身并不包含任何领域逻辑,仅负责协调领域模型对象,通过它们的领域能力来组合完成一个完整的应用目标。应用服务作为应用外观,仅仅是领域层的一个入口点,通过它可以降低客户程序与领域层实现之间的依赖。作为领域模型对象的包装,它自身不应该包含任何领域逻辑。由此可得到应用服务设计的第一条准则:不包含领域逻辑的业务服务应被定义为应用服务。

如果参考 Robert Martin 提出的整洁架构思想,领域驱动分层架构的应用层可对应整洁架构内核中的用例(Use Case)层。不过,领域驱动设计强调应用服务虽然对外表现了应用业务逻辑(Application Business Rule),但达成应用目标的实现逻辑需要分配给领域层的领域模型对象。

无论六边形架构还是整洁架构,都认为是网关(即六边形架构中的适配器)打通了内部领域核心与外部资源和框架的通道。网关封装了外部资源访问与框架依赖的实现逻辑,属于外部的基础设施层。北向网关属于外部依赖内部,南向网关则相反,属于内部依赖外部。因此,要让南向网关满足整洁架构思想,避免内部的领域逻辑依赖于外部的基础设施,就需要为南向网关引入抽象和依赖注入。

在领域驱动设计中,属于南向网关的资源库,其抽象常被视为领域层的一部分;不止于此,整个“南向网关”的抽象其实亦可视为组成领域层的一部分,例如访问第三方服务的 HttpClient,发送通知的抽象服务接口。考虑到分层与模块之间的关系,我在《领域驱动战略设计》中,给出了与领域驱动设计思想对应的代码模型。在这个代码模型中,我将网关分为了 interfaces 与 gateways 两个包,前者仅定义了网关的抽象,后者则提供对应的实现。对应到分层架构,网关的抽象归属于领域层,网关的实现归属于基础设施层。

在考虑业务逻辑与具体技术实现之间的协作时,可以将南向网关的抽象既注入到领域服务或应用服务。领域服务与南向网关抽象之间的协作关系属于同层之间的依赖,应用服务与南向网关抽象之间的协作属于外层调用内层,二者都没有违背整洁架构思想。这意味着,领域逻辑与技术实现的隔离和结合既可以在领域层完成,也可以在应用层完成;那么,应用服务除了能对细粒度的领域逻辑进行包装之外,它还能提供其余什么价值呢?

一个完整的业务用例场景,多数时候不仅限于领域逻辑,也不仅限于访问数据库或者其他第三方服务,往往还需要和如下逻辑进行协作:

  • 消息验证
  • 错误处理
  • 监控
  • 事务
  • 认证与授权
  • ……

《领域驱动设计模式、原理与实践》一书将以上内容视为基础架构问题。这些关注点与具体的领域逻辑无关,且在整个系统中,会作为重用模块被诸多服务调用。调用时,这些关注点是与领域逻辑交织在一起的,因此这些关注点都属于横切关注点

从面向切面编程(Aspect-Oriented Programming,AOP)的角度看,所谓“横切关注点”就是那些在职责上是内聚的,但在使用上又会散布在所有对象层次中,且与所散布到的对象的核心功能毫无关系的关注点。与“横切关注点”对应的是“核心关注点”,就是与系统业务有关的领域逻辑。例如,订单业务是核心关注点,提交订单时的事务管理以及日志记录则是横切关注点:

public class OrderAppService {@Serviceprivate PlacingOrderService placingOrderService;// 事务为横切关注点@Transactional(propagation=Propagation.REQUIRED) public void placeOrder(Order order) { try {orderService.execute(order);} catch (InvalidOrderException ex | Exception ex) {// 日志为横切关注点logger.error(ex.getMessage());// ApplicationException 派生自 RuntimeException,事务会在抛出该异常时回滚throw new ApplicationException("failed to place order", ex);}}
}

横切关注点与具体的业务无关,它与核心关注点在逻辑上应该是分离的。为保证领域逻辑的纯粹性,应尽量避免将横切关注点放在领域模型对象中。于是,应用服务就成了与横切关注点协作的最佳位置。由此,可以得到应用服务设计的第二条原则:与横切关注点协作的服务应被定义为应用服务。

应用服务与领域服务的选择

如前所述,应用服务不应该包含任何领域逻辑,同时,它又将作为一个外观服务,负责封装多个领域模型对象之间的协作。那么,将多个领域行为组合起来的协调行为,究竟算不算是领域逻辑呢?例如,对于“下订单”用例而言,如果我们在各自的领域对象中定义了如下行为:

  • 验证订单是否有效
  • 提交订单
  • 移除购物车中已购商品
  • 发送邮件通知买家

这些行为的组合正好满足了“下订单”这个完整用例的需求,同时也为了保证客户调用的简便性,我们需要协调这四个领域行为。这一协调行为牵涉到不同的领域对象,因此只能定义为服务。那么,这个服务应该是应用服务,还是领域服务?

《领域驱动设计模式、原理与实践》一书将这种封装认为是与领域的交互。该书作者给出了一个判断标准:

决定一系列交互是否属于领域的一种方式是,提出“这种情况总是会出现吗?”或者“这些步骤无法分开吗?”的问题。如果答案是肯定的,那么这看起来就是一个领域策略,因为那些步骤总是必须一起发生。然而,如果那些步骤可以用若干方式重新组合,那么可能它就不是一个领域概念。

我想,这一判断标准是基于“任务编制”得出的结论。如果领域逻辑的步骤必须一起发生,就说明这些逻辑不存在“任务编制”的可能,因为它们在本质上是一个整体,只是基于单一职责原则与分治原则,需要进行分解,做到对象的各司其职而已。如果领域步骤可以用若干方式重新组合,就意味着可以有多种方式进行“任务编制”。因此,任务编制逻辑就属于应用逻辑的范畴,编制的每个任务则属于领域逻辑的范畴,前者由应用服务来承担,后者由领域模型对象来承担。

Eric Evans 用另一种玄而又玄的说法印证了该判断标准:“应用服务是协调者,它们只是负责提问,而不负责回答,回答是领域层的工作。”注意,对所谓“提问”和“回答”的理解,要站在一个完整用例场景的高度来阐释。当客户端发来请求要执行一个完整的用例场景时,作为协调者的应用服务只负责安排任务,至于任务该怎么做,就是领域模型对象要完成的工作。这实际上是业务价值(Why)与业务功能(What)之间的关系。对于一个用例场景,需要为参与者提供业务价值,该价值由应用服务提供;要实现这一业务价值,需要若干业务功能按照某种顺序进行组合,组合的顺序就是编制,编制的业务功能就是回答问题的领域模型对象。

要基于这一标准对应用服务与领域服务做出正确判断,更多地还是依靠你对设计的感觉。因为价值与功能在不同的层次会产生一种层层递进的递归关系。例如下订单是业务价值,验证订单就是实现该业务价值的业务功能;然而再进一层,又可以将验证订单视为业务价值,而将验证订单的配送地址有效性作为实现该业务价值的业务功能。至于前面提到的“任务编制”,其实也存在歧义,即使在领域服务中,也存在任务编制的可能,这实际取决于你对任务层次的定位。这还真是剪不断理还乱了。

让我们回归本质,回到对“领域”这个词的理解。在领域驱动设计这个大背景下,领域其实与软件系统服务的行业有关,如金融行业、制造行业、医疗行业、教育行业等。在领域驱动设计的战略阶段,又将整个系统的领域分解为核心领域与子领域,它们解决的是不同的问题域。在解决方案域,应用服务和领域服务都属于一个具体的限界上下文,它们又必然映射到问题域中某一个子领域上。由此可得到一个推论:领域逻辑就是对应子领域包含的业务知识和业务规则,应用逻辑则是为了完成完整用例而包含的除领域逻辑之外的其他业务逻辑,包括作为基础架构问题的横切关注点,也可能包含对非领域知识相关的处理逻辑,如对输入、输出格式的转换等。

Eric Evans 用银行转账的案例来讲解应用逻辑与领域逻辑的差异。他说:“资金转账在银行领域语言中是一项有意义的操作,而且它涉及基本的业务逻辑。”这就说明资金转账属于领域逻辑。至于应用服务该做什么,他又说道:“如果银行应用程序可以把我们的交易进行转换并导出到一个电子表格文件中,以便进行分析,那么这个导出操作就是应用服务。‘文件格式’在银行领域中是没有意义的,它也不涉及业务规则。”

因此,到底选择应用服务还是领域服务,就看它的实现中到底是应用逻辑的范畴,还是领域逻辑的范畴。一个简单的判断标准在于这段代码蕴含的知识是否与它所处的限界上下文要解决的问题域直接有关?如此说来,针对“下订单”用例而言,在前面列出的四个领域行为中,只有“发送邮件”与购买子领域没有关系,因此可考虑将其作为要编制的任务放到应用服务中。如此推导出来的订单应用服务实现为:

public class OrderAppService {@Serviceprivate PlacingOrderService placingOrderService;// 此时将 NotificationService 视为基础设施服务@Serviceprivate NotificationService notificationService;// 事务为横切关注点@Transactional(propagation=Propagation.REQUIRED) public void placeOrder(Order order) { try {orderService.execute(order);notificationService.send(notificationComposer.compose(order));} catch (InvalidOrderException ex | Exception ex) {// 日志为横切关注点logger.error(ex.getMessage());// ApplicationException 派生自 RuntimeException,事务会在抛出该异常时回滚throw new ApplicationException("failed to place order", ex);}}
}

即使如此,应用逻辑与领域逻辑的边界线依旧微妙难分。

我注意到《领域驱动设计》中的两段描述。其一:

很多领域服务或应用服务是在实体和值对象的基础上建立起来的,它们的行为类似于将领域的一些潜在功能组织起来以执行某种任务的脚本。实体和值对象往往由于粒度过细而无法提供对领域层功能的便捷访问。

其二:

在大型系统中,中等粒度的、无状态的服务更容易被复用,因为它们在简单的接口背后封装了重要的功能。……由于应用层负责对领域对象的行为进行协调,因此细粒度的领域对象可能会把领域层的知识泄露到应用层中。这产生的结果是应用层不得不处理复杂的、细致的交互,从而使得领域知识蔓延到应用层或用户界面代码当中,而领域层会丢失这些知识。明智地引入领域服务有助于在应用层和领域层之间保持一条明确的界限。

综合这两段话,我们可以隐约探索到分辨应用服务与领域服务的真相。第一段提到“实体和值对象往往由于粒度过细而无法提供对领域层功能的便捷访问”,第二段又提到“细粒度的领域对象可能会把领域层的知识泄露到应用层中”,无论从隐藏细节的角度,还是从便捷访问的角度,在领域层,领域服务都成了当仁不让的最佳选择。

而在第一段中,Eric Evans 又说应用服务和领域服务都是“执行某种任务的脚本”。任务脚本可以理解为对任务的编制,只是应用服务和领域服务处理的任务层级并不相同罢了。再结合第二段的最后一句“明智地引入领域服务有助于在应用层和领域层之间保持一条明确的界限”,我们有理由得到如下结论:

  • 细粒度的领域对象包括实体、值对象以及领域服务,但为了避免领域层知识泄漏到应用层中,应在领域层定义中等粒度的领域服务,它的实现可以认为是对细粒度领域服务、聚合的任务编制
  • 理想状态下,应用服务应该只与中等粒度的领域服务协作,它对任务的编制,实则就是对领域服务的编制

若同意这一结论,说明应用服务中只能包含两部分内容:领域服务、横切关注点。如此设计自然逃脱不了僵化的嫌疑,但殊不知我是在为设计做减法。若设计者能够充分辨别应用逻辑与领域逻辑之间的差别,突破这一约束也未尝不可。一旦你拥有了足够丰富的设计知识和设计经验,就意味着你可以正确地做出适合当前场景的设计决策与判断。若无法做到,不妨从一些相对固化的简单原则开始做起,这算是从新手到专家所必须经历的成长过程。

影响应用服务的因素

一旦对应用服务的设计进行了约束,要分辨应用服务和领域服务的区别就变得容易了许多。然而,软件设计就是这样,当你因为某种干扰因素而做出一种设计决策时,在消除了这一干扰因素的同时,另外一些原来不曾显现的干扰因素又可能浮现出来。既然应用服务的实现代码只能包含横切关注点,也只能与领域层的领域服务协作,那就需要我们对横切关注点做出正确判断,同时还需要明确领域服务的设计粒度。

横切关注点的判断

要判断一个服务是否为应用服务,需要明确什么是“横切关注点”。前面已经明确给出了“横切关注点”的定义,但是,在判断横切关注点以及整合横切关注点时,除了前面提到的事务、监控、身份验证与授权没有争议之外,社区对如下关注点普遍存在困惑与纠结。

日志

毫无疑问,日志属于横切关注点的范畴。然而,倘若将日志功能仅仅放在应用层,又可能无法准确详细地记录操作行为与错误信息。很多语言都提供了基础的日志框架,将日志混杂在领域对象中,会影响领域的纯粹性,也带来了系统与日志框架的耦合,除非采用 AOP 的方式。目前看来,这是一种编码取舍,即倾向于代码的纯粹性,还是代码的高质量。我个人更看重代码的质量,尤其是丰富的日志内容有助于运维排错,因此可考虑将作为横切关注点之一的日志功能放在领域服务中,算是上述应用服务边界定义的特例。

当然,这个划分并非排他性的。在应用服务中,同样需要调用日志功能,只是记录的信息与粒度和领域服务不尽相同罢了。

验证

如果是验证外部客户传递过来的消息,例如对 RESTful 服务的 Request 请求的验证,则该验证功能属于横切关注点,对它的调用就应该放在应用服务(亦可考虑由远程服务自己承担)。如果验证逻辑属于一种业务规则,例如验证订单有效性,就应该将验证逻辑放在领域层,以便于领域模型对象调用。

异常处理

与领域逻辑有关的错误与异常,应该以自定义异常形式表达业务含义,并被定义在领域层。此外,如果该异常表达了业务含义,为了保证业务的健壮性,可在领域层中将异常定义为受控异常(Checked Exception)。由于该异常与业务有关,即使被定义在方法接口中,也不存在异常对接口的污染,即可以将异常视为接口契约的一部分。但是,在领域服务中,不应该将与业务无关的受控异常定义在领域服务的方法中,否则就会导致业务逻辑与技术实现的混合。

在应用层,应尽可能保证应用服务的通用性,因而需要在应用服务中捕获与业务有关的自定义异常,然后将其转换为标准格式的异常之后再抛出。例如,可统一定义为应用层的标准异常 ApplicationException,然后在 message 或 cause 中包含具体的业务含义。因此,针对异常处理,只有这部分与业务无关的处理与转换功能,才属于横切关注点的范畴,并放在应用层,其余异常处理逻辑都属于领域层。

基础设施服务

除了上述纠结的横切关注点之外,我们还要注意基础设施服务与横切关注点之间的区别。在领域驱动设计中,基础设施服务作为技术服务,被定义为网关。从代码实现的角度考虑,南向网关代表了一个内聚的技术实现,可以被抽象为接口;横切关注点则是一些钩子方法,会在领域行为方法的前后被执行,因此难以抽象为接口。显然,基础设施服务就像提供的其他基础功能一般,可以很容易被重用,而横切关注点由于会和领域逻辑纠缠在一起,很难剥离出单独的横切关注点代码,除非采用面向切面编程。

遵循应用服务的设计原则,它除了和领域服务进行协作之外,就只是包含了横切关注点,这就说明应用服务甚至都不应该依赖于提供基础设施服务的南向网关。这样的设计约束充分保证了应用服务的简单性。因此,只要判断某个逻辑属于基础设施服务,就应该首先考虑与领域服务协作,而非应用服务。例如,邮件通知服务就属于典型的基础设施服务。既然如此,针对订单应用服务的实现,就应该将通知服务转移到 PlacingOrderService 领域服务中。事实上,在前面修改后的订单应用服务代码中,代码 notificationComposer.compose(order) 放在应用服务中本身也不太合适,因为将订单内容转换为邮件通知内容,更像是领域逻辑,而非应用逻辑。

领域服务的设计粒度

在领域层中,为了保证聚合内部实体与值对象的纯粹性,我们将与外部资源抽象之间的协作推给了领域服务;为了避免出现贫血模型和过程式的事务脚本,我们要求定义带有动词的领域服务,使得领域服务在正确表达领域行为特征的同时,粒度也变得更细。这时的领域服务其本质更像是一个函数,没有状态,单一职责,体现的是领域逻辑的行为特征。

但是,在面向对象设计中,粒度大小与简单设计需要平衡。若要二者兼得,需要在细粒度对象之上再引入一层封装:一边是纷繁的实现细节,一边是干净利落的接口。这正是引入中等粒度领域服务的由来。中等粒度的领域服务实质上是对更细粒度的领域模型对象之间的流程编制,它的主要作用在于协调多个领域对象,尤其是多个细粒度领域服务之间的协作。

还记得《理解领域模型》一节给出的“订阅课程”业务场景的案例吗?当时我以可视化的时序图方式给出了各个对象角色之间的协作关系:

79fc89bc-2ae8-4074-b9b5-4c09f2046b6c.jpg

显然,图中蓝色的应用服务 CouseAppService 划定了一条远程服务与领域层之间的界限,使得远程服务无需了解课程订阅领域逻辑的实现细节。课程与期望列表属于两个不同的限界上下文,但它们又都处于同一个进程边界内,因此它们之间的协作通过应用服务来完成。领域服务 SubscriptionValidation 仅仅实现了对订阅的验证功能。它是一个细粒度服务,部分验证的逻辑委派给了 Course 聚合,避免了贫血模型。持久化与邮件通知都属于基础设施服务,分别由资源库和邮件通知南向网关完成。

领域服务 SubscribeCourseService 并没有履行具体的业务职责,它只是将多个领域对象组合起来,进行业务流程的编制。观察时序图,你会发现由该服务发出的方法调用是最多的。这就是所谓的中等粒度领域服务。它在应用层和领域层之间划定了一条明确的界限,也使得应用服务 CouseAppService 得偿所愿,成为一个没有领域逻辑的外观服务。采用时序图的可视化方式,可以观察应用服务发起的调用,即图中涂为深蓝色的地方。很明显,应用服务发起的调用越少,包含领域逻辑的可能性就越小。

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

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

相关文章

Unity3D中刚体、碰撞组件、物理组件的区别详解

前言 Unity3D提供了丰富的功能和组件,其中包括刚体、碰撞组件和物理组件。这些组件在游戏开发中起着非常重要的作用,能够让游戏世界更加真实和有趣。本文将详细介绍这三种组件的区别以及如何在Unity3D中实现它们。 对惹,这里有一个游戏开发…

MAC M1安装vmware和centos7虚拟机并配置静态ip

一、下载vmware和centos7镜像 1、VMWare Fusion 官网的下载地址是:下载地址 下载好之后注册需要秘钥,在官网注册后使用免费的个人秘钥 2、centos7 下载地址: https://biosyxh.cn:5001/sharing/pAlcCGNJf 二、虚拟机安装 直接将下…

算法沉淀——多源 BFS(leetcode真题剖析)

算法沉淀——多源 BFS(leetcode真题剖析) 01.矩阵02.飞地的数量03.地图中的最高点04.地图分析 多源 BFS 是指从多个源点同时进行广度优先搜索的算法。在传统的 BFS 中,我们通常从一个起始点开始,逐层遍历所有的相邻节点。而在多…

探索AI视频生成新纪元:文生视频Sora VS RunwayML、Pika及StableVideo——谁将引领未来

探索AI视频生成新纪元:文生视频Sora VS RunwayML、Pika及StableVideo——谁将引领未来 sora文生视频,探索AI视频生成新纪元 由于在AI生成视频的时长上成功突破到一分钟,再加上演示视频的高度逼真和高质量,Sora立刻引起了轰动。在S…

408计算机网络--基础概论

学习计算机网络走以前需要首先明白一个大的概念,计算机网络通常分为通信子网(实现数据通信)和资源子网(实现资源共享/数据处理)七层妖塔 计算机网络:是一个将分散的、具有独立功能的计算机系统&#xff0…

Rabbitmq入门与应用(三)-RabbitMQ开发流程

RabbitMQ开发流程 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId> </dependency>配置MQ 最简配置 spring:rabbitmq:host: mq的安装机器ipport: 5672username: ad…

忘记管理员密码

1、在/home/jenkins/config.xml中删除&#xff1a; <useSecurity>true</useSecurity><authorizationStrategy class"hudson.security.FullControlOnceLoggedInAuthorizationStrategy"><denyAnonymousReadAccess>false</denyAnonymousRea…

Hack The Box-Office

端口扫描&信息收集 使用nmap对靶机进行扫描 nmap -sC -sV 10.10.11.3开放了80端口&#xff0c;并且注意到该ip对应的域名为office.htb&#xff0c;将其加入到hosts文件中访问之 注意到扫描出来的还有robots文件&#xff0c;经过尝试后只有administrator界面是可以访问的 …

详解平面点云面积计算

部分代码展示&#xff1a; &#xff08;1&#xff09;利用格网法计算面积&#xff1a; //&#xff08;2&#xff09;测试使用格网法计算平面点云面积 void main() {char *inputpath "D:\\testdata\\data.txt";vector<pcl::PointXYZ> points ReadPointXYZIn…

Java 21 新特性的扫盲级别初体验

一、前言 JDK 21 于 2023 年 9 月发布&#xff0c;作为目前讨论热度最高的JDK&#xff0c;虽然大家都开玩笑说你发任你发&#xff0c;我用Java8&#xff0c;但是作为一个Javaer&#xff0c;对JDK21的新特性还是要有所了解的。 以下是 JDK 21 的新功能列表&#xff1a; 虚拟线…

element-plus日期选择器2次封装

预期效果 官网默认样式&#xff1a; 修改后的样式&#xff1a; 代码实现 DatePicker.vue <template><div class"date-picker-container"><el-date-picker v-model"date" change"handleChange" type"date" value-for…

CMake无Name和Value部分界面

鼠标会变成以下 拉开后就 出现想要的部分

PTA-九九乘法表

从键盘输入n&#xff08;n为1~9&#xff09;&#xff0c;输出九九乘法表的前n行&#xff0c;每个式子按“a * b c”形式输出&#xff0c;其中a和b各占1位列宽&#xff0c;c占4位列宽并左对齐。 输入格式: 一个1~9之间的整数n&#xff0c;如&#xff1a;9 输出&#xff1a; …

vue3项目配置按需自动引入自定义组件unplugin-vue-components

我们通常在项目中&#xff0c;需要手动引入自定义的各种组件&#xff0c;如果涉及的页面功能比较多的话&#xff0c;光是import的长度都能赶上春联了。 如果&#xff0c;能有一个插件帮我们实现自动引入&#xff0c;是不是要谢天谢地了呢&#xff1f; 接下来就进入我们的主角u…

centos中安装go

安装过程 &#xff08;1&#xff09;源码二进制下载地址 wget https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz &#xff08;2&#xff09;将下载的二进制包解压至 /usr/local目录。 tar -C /usr/local/ -xzf go1.13.5.src.tar.gz &#xff08;3&#xff09;设置环…

XR行业首家|李未可科技通过深度合成服务算法备案

2月18日&#xff0c;国家网信办发布第四批深度合成服务算法备案。 根据《互联网信息服务深度合成管理规定》第十九条规定&#xff0c;具有舆论属性或者社会动员能力的深度合成服务提供者&#xff0c;应当按照《互联网信息服务算法推荐管理规定》履行备案和变更、注销备案手续。…

人工智能_普通服务器CPU_安装清华开源人工智能AI大模型ChatGlm-6B_003---人工智能工作笔记0098

前面的环境安装差不多了,这里我没有安装git,因为我认为用不到,好下面去下载算法: 首先是算法下载: https://codeload.github.com/THUDM/ChatGLM-6B/zip/refs/heads/main 算法的下载连接是这里: 可以看到下载以后得到这个ChatGLM-6B-main这个算法压缩包 然后我们再去: 然后…

Guitar Pro8最新官方破解版下载步骤教程

使用Guitar Pro 8的步骤如下&#xff1a; 安装Guitar Pro 8软件。双击打开安装包&#xff0c;按照提示完成安装过程。打开软件后&#xff0c;使用左侧工具栏上的按钮输入谱号和拍号。点击左侧工具栏的和弦图按钮&#xff0c;输入所需要的和弦。使用鼠标和键盘在乐谱中输入乐曲…

MCAL知识点(二十七):TC275如何通过GPT12实现ABZ解码

目录 1、概述 2、代码实现 1、概述 GPT12 - General Purpose Timer Unit (GPT12):通用定时器单元,具备较为灵活的定时器结构,可以用来做定时器、事件计数、脉冲宽度测量、产生PWM、频率调制、ABZ编码器增量测量。文章记录一下如何通过GPT12实现编码器ABZ信号的测量。 注意…

修复 Android 手机陷入恢复模式的 5 种方法

您的手机卡在 Android Recovery 模式且无法退出此模式&#xff1f;无论您按什么按钮组合&#xff0c;甚至在取出并重新插入电池后重新启动手机&#xff0c;手机都会启动回到恢复模式吗&#xff1f; Android卡在recovery模式的情况并不罕见&#xff0c;各种品牌的Android手机都…