Day929.运用自动化工具诊断分析Sharing项目 -系统重构实战

运用自动化工具诊断分析Sharing项目

Hi,我是阿昌,今天学习记录的是关于运用自动化工具诊断分析Sharing项目的内容。

之前文章的两个遗留系统常用的分析工具:ArchUnit Dependencies 依赖分析工具

了解了它们的基本使用方法,但是实际落地到项目中,经常会遇到一些问题,比如:

  1. 代码散落各处,约束规则不好写?
  2. 结合架构设计,怎么来设计约束规则?
  3. 约束规则怎么应用到自动化分析工具上?

下面用 ArchUnit 和 Dependencies 对 Sharing 项目进行一次整体分析。

这个分析由五部分组成。

  • 第一部分,将代码结构按新的架构设计进行调整
  • 第二部分,根据代码结构以及新架构设计定义出依赖规则
  • 第三部分,将依赖规则转化成 Dependencies 的 Rule 规则,然后进行扫描分析
  • 第四部分,将依赖规则转化成 ArchUnit 的用例,进行扫描分析
  • 最后,总结现有的代码结构按未来架构设计需要重构的问题清单,作为下一阶段代码重构的输入

一、以新架构设计来组织代码

首先,回顾一下 Sharing 项目目前代码的方式。

如下图所示,所有的代码是以技术维度来组织。

例如把所有页面都放在 ui 的包下,或者把所有的模型都放在 model 下

Day929.运用自动化工具诊断分析Sharing项目 -系统重构实战_第1张图片

这个时候就会遇到代码散落在各处约束规则不好写的问题。

如果不先以未来架构设计的方式来组织代码,那么所有依赖规则的编写只能细分到类,不能按包的维度来编写。

这样首先会导致用例增多,其次规则也会比较分散,给后续维护带来不便。

所以为了更好地进行分析,首先会先按未来的架构设计组织代码。


来看看梳理的新组件划分架构。

在新的架构设计中,横向维度划分了 3 个分层,纵向维度划分了 3 个业务组件、2 个功能组件以及 3 个技术组件。

Day929.运用自动化工具诊断分析Sharing项目 -系统重构实战_第2张图片
根据新的架构图,重新划分新的包结构,如下表所示。


注意,需要让代码的架构以及名称与架构图的设计对应上,这样便于理解和维护。

Day929.运用自动化工具诊断分析Sharing项目 -系统重构实战_第3张图片

接下来,就可以按照新的代码结构进行调整了。

这个时候要注意借助 IDE 的安全重构方法来移动代码,避免手工移动。

比如,可以通过 Move Class 的方法(选中文件后按下 F6 快捷键或者点击 Refactor->Move Class 菜单)进行移动,这样编辑器会自动帮我们调整相关的引用,调整后 Sharing 新的目录结构是后面这样。

Day929.运用自动化工具诊断分析Sharing项目 -系统重构实战_第4张图片

在实际项目落地中,这个步骤有三点注意事项。

  • 第一点,这个步骤最好可以拉通相关的开发、架构等干系人(我们可以通过版本管理系统来查看之前这个文件主要的维护人)一起进行梳理。因为有些文件我们能从类名判断它属于哪个组件,但是有些文件如果之前设计得不好,还需要跟相关的同学确认。

  • 第二点,移动文件会触发 CI 判定这些文件是新增文件,进而触发增量扫描,CI 会将以前遗留的很多问题也扫描出来。但注意,这些问题不是因为移动文件产生的,而是原本就存在于代码库中。需要和相关的干系人进行澄清说明,以免移动后的代码提交无法入库

  • 第三点,工程目录结构的调整会涉及到大量文件的位置变化,需要在团队中进行拉通,避免很多临时分支还在基于旧的代码目录结构进行开发,否则后期会产生大量的代码冲突,解决成本非常高


二、Sharing 架构代码规则设计

基于新的代码结构,就可以设计架构约束规则了。

首先,回顾一下 Sharing2.0 架构的两个重要约束原则。

  • 纵向规则上层组件可以依赖下层组件,下层组件不能反向依赖上层的组件。
  • 横向规则业务组件之间不能有直接的依赖,功能及技术组件之间尽量减少依赖。

基于这两个原则,结合代码结构设计出新的架构核心的 5 个约束规则,可以参考后面这张表格。

Day929.运用自动化工具诊断分析Sharing项目 -系统重构实战_第5张图片

梳理出这些约束规则后,就可以将这些规则转换成工具的规则进行分析了。


三、Dependencies 依赖分析

首先,需要定义各个组件的 Scope,这一步相当于是定义各个组件的范围。

如下图所示,新增一个 Scope 后,可以选择在项目中对应的的目录,当然也可以直接在 Pattern 中写正则表达式来进行定义。

Day929.运用自动化工具诊断分析Sharing项目 -系统重构实战_第6张图片

定义好 Scope 之后,我们将 5 个核心约束规则转化为 Dependencies 的依赖规则。

下图中的 Deny usages of feature in library 表示 feature 范围下的代码不能被 library 范围下的代码直接依赖。

Day929.运用自动化工具诊断分析Sharing项目 -系统重构实战_第7张图片

当完成依赖规则定义后,Dependencies 扫描结果会自动化将所有的异常依赖标记为红色,如下图所示。

Day929.运用自动化工具诊断分析Sharing项目 -系统重构实战_第8张图片

从分析结果可以看出,目前 Sharing 项目有四个组件存在依赖问题,需要解耦,分别为文件组件、消息组件、基座组件以及日志组件。

打开具体有异常的类,IDE 还会自动在代码加上警告的红色线。

Day929.运用自动化工具诊断分析Sharing项目 -系统重构实战_第9张图片
这是 Dependencies 功能与 IDE 高度集成带来的好处,可以在日常开发中就注意到架构约束的问题,避免破坏架构规则。


四、ArchUnit 分析

接下来,ArchUnit 的分析思路也是定义包范围,并将 5 个约束转化为依赖规则。

首先,可以参考 Dependencies 的规则封装形式,将 ArchUnit 的语法也做进一步的封装,这样可以提高代码的复用性,降低后续用例的维护成本。

//某个包只能依赖另外一个包
private static ClassesShouldConjunction target_package_not_dependOn_other_package(String targetPackage, String... otherPackages) {
    return noClasses().that().resideInAPackage(targetPackage)
            .should().dependOnClassesThat().resideInAnyPackage(otherPackages);
}
//某个包只能依赖它自己,不能依赖其他包
private static ClassesShouldConjunction target_package_only_dependOn_itSelf(String targetPackage) {
    return classes().that().resideInAPackage(targetPackage)
            .should().dependOnClassesThat().resideInAPackage(targetPackage);
}

接着,定义不同的分层和组件。因为已经按未来的架构调整了工程目录,所以这一步可以很方便地定义出对应的分层和组件范围。

private static final String BASE = "com.jkb.junbin.sharing.";
private static final String FEATURE = BASE + "feature..";
private static final String FUNCTION = BASE + "function..";
private static final String LIBRARY = BASE + "library..";
private static final String FILE_BUNDLE = BASE + "feature.file..";
private static final String MESSAGE_BUNDLE = BASE + "feature.message..";
private static final String ACCOUNT_BUNDLE = BASE + "feature.account..";

最后将 5 个约束规则转化为 ArchUnit 的架构约束代码。

这个时候前面封装的约束规则方法就起作用了,每个用例只需要传递相关的组件定义就可以了。

//规则1:library包下的类只能依赖自己包下的类,不能依赖function包或者feature包下的类
@ArchTest
public static final ArchRule library_should_only_dependOn_itself =
        target_package_not_dependOn_other_package(LIBRARY,FUNCTION,FEATURE);

//规则2:function包下的类不能依赖feature包下的类
@ArchTest
public static final ArchRule function_should_not_dependOn_feature =
        target_package_not_dependOn_other_package(FUNCTION, FEATURE);

//规则3:account包下的类不能依赖file或者message包下的类
@ArchTest
public static final ArchRule account_bundle_should_not_dependOn_other_bundle =
        target_package_not_dependOn_other_package(ACCOUNT_BUNDLE, FILE_BUNDLE, MESSAGE_BUNDLE);

//规则4:file包下的类不能依赖account或者message包下的类       
@ArchTest
public static final ArchRule file_bundle_should_not_dependOn_other_feature =
        target_package_not_dependOn_other_package(FILE_BUNDLE, MESSAGE_BUNDLE, ACCOUNT_BUNDLE);

//规则5:message包下的类不能依赖account或者file包下的类
@ArchTest
public static final ArchRule message_bundle_should_not_dependOn_other_bundle =
        target_package_not_dependOn_other_package(MESSAGE_BUNDLE, FILE_BUNDLE, ACCOUNT_BUNDLE);

编写完成后,可以直接执行这 5 个用例。

用例执行的结果是后面这样。

Day929.运用自动化工具诊断分析Sharing项目 -系统重构实战_第10张图片

从日志结果可以看出,目前 Sharing 项目有 4 个约束规则不通过,分别是文件组件存在横向依赖、消息组件存在横向依赖、功能组件存在反向依赖以及基础组件存在反向依赖。


五、总结

ArchUnit 和 Dependencies 依赖分析功能对 Sharing 项目进行了一次分析。

为了更加方便地编写架构规则,需要将代码架构先按未来的架构设计进行调整。

接着只需要定义组件的范围以及约束规则就可以了。

最后结合工具辅助,梳理出 Sharing 按未来架构设计需要处理的问题清单。

Day929.运用自动化工具诊断分析Sharing项目 -系统重构实战_第11张图片

开头的 3 个问题你答案:

  • 第一个问题,代码散落各处,约束规则不好写?
    • 对此,需要先按未来的架构设计调整代码结构,方便约束规则的设计
  • 第二个问题,结合架构设计,怎么来设计约束规则?
    • 需要结合架构原则以及代码结构来进行设计
  • 最后一个问题,约束规则怎么应用到自动化分析工具上?
    • 需要将约束规则转换成各个工具上的编写规则

你可能感兴趣的:(业务设计,自动化,重构,java,android)