移动应用遗留系统重构(7)- 解耦重构演示篇(一)+视频演示

前言

移动应用遗留系统重构(5)- 重构方法篇中我们分享了重构流程,主要为4个操作步骤。

  1. 识别一个内聚的包
  2. 解除该包的异常依赖
  3. 移动该包对应的代码及资源到新的模块
  4. 包解耦验收

移动应用遗留系统重构(6)- 测试篇中我们为CloudDisk补充了一组基本的冒烟测试。有了基本的测试守护后,本篇我们将挑选library(基础组件库)及file(文件业务模块)2个包进行重构演示。文章中包含操作的流程,同时了录制了视频。

library模块重构代码演示视频:mp.weixin.qq.com/s/C0nQRbgmp…

file模块重构代码演示视频:mp.weixin.qq.com/s/xbAIu6bWS…

安全重构演示

library包重构

  1. 依赖分析

通过分析我们发现library包存在对上层模块的反向依赖,该依赖为异常依赖,须要解除。

  1. 安全重构

重构前代码:

public class HttpUtils {
    public static void post(String url) {
        //发送http post请求,需要用到userId做标识
        String params = UserController.userId;
    }
    public static void get(String url) {
        //发送http get请求,需要用到userId做标识
        String params = UserController.userId;
    }
}

复制代码

重构手法:提取方法参数

重构后代码:

public class HttpUtils {
    public static void post(String url, String userId) {
        //发送http post请求,需要用到userId做标识
        String params = userId;
    }
    public static void get(String url, String userId) {
        //发送http get请求,需要用到userId做标识
        String params = userId;
    }
}
复制代码
  1. 代码移动

代码移动至独立的library,加上对应的Gradle依赖:

移动后模块结构如下:

  1. 功能验证

执行冒烟测试,验证功能

./gradlew app:testDebug --tests SmokeTesting

复制代码

代码演示:mp.weixin.qq.com/s/C0nQRbgmp…

具体的代码:github链接

file包重构

  1. 依赖分析

通过分析我们发现file包存在横向bundle模块的依赖,该依赖为异常依赖,须要解除。

  1. 安全重构

重构前代码:

public class FileController {
    public List<FileInfo> getFileList() {
        return new ArrayList<>();
    }

    public FileInfo upload(String path) {
        //上传文件
        LogUtils.log("upload file");
        HttpUtils.post("http://file", UserController.userId);
        return new FileInfo();
    }

    public FileInfo download(String url) {
        //下载文件
        if (!UserController.isLogin) {
            return null;
        }
        return new FileInfo();
    }
}
复制代码

重构手法:

2.1 抽取getUserId、isLogin方法

重构后代码如下:

public class FileController {
    public List<FileInfo> getFileList() {
        return new ArrayList<>();
    }

    public FileInfo upload(String path) {
        //上传文件
        LogUtils.log("upload file");
        HttpUtils.post("http://file", getUserId());
        return new FileInfo();
    }

    public FileInfo download(String url) {
        //下载文件
        if (!isLogin()) {
            return null;
        }
        return new FileInfo();
    }

    private String getUserId() {
        return UserController.userId;
    }

    private boolean isLogin() {
        return UserController.isLogin;
    }
}

复制代码

2.2 抽取代理类,UserState

重构后代码如下:

public class FileController {
    private final UserState userState = new UserState();

    public List<FileInfo> getFileList() {
        return new ArrayList<>();
    }

    public FileInfo upload(String path) {
        //上传文件
        LogUtils.log("upload file");
        HttpUtils.post("http://file", userState.getUserId());
        return new FileInfo();
    }

    public FileInfo download(String url) {
        //下载文件
        if (!userState.isLogin()) {
            return null;
        }
        return new FileInfo();
    }
}
复制代码

2.3 抽取接口

重构后代码如下:

public class FileController {
    private final UserState userState = new UserStateImpl();

    public List<FileInfo> getFileList() {
        return new ArrayList<>();
    }

    public FileInfo upload(String path) {
        //上传文件
        LogUtils.log("upload file");
        HttpUtils.post("http://file", userState.getUserId());
        return new FileInfo();
    }

    public FileInfo download(String url) {
        //下载文件
        if (!userState.isLogin()) {
            return null;
        }
        return new FileInfo();
    }
}
复制代码

2.4 提取构造函数,依赖接口注入

重构后代码如下:

public class FileController {
    private final UserState userState;

    public FileController(UserState userState) {
        this.userState = userState;
    }

    public List<FileInfo> getFileList() {
        return new ArrayList<>();
    }

    public FileInfo upload(String path) {
        //上传文件
        LogUtils.log("upload file");
        HttpUtils.post("http://file", userState.getUserId());
        return new FileInfo();
    }

    public FileInfo download(String url) {
        //下载文件
        if (!userState.isLogin()) {
            return null;
        }
        return new FileInfo();
    }
}

复制代码

同样FileFragment我也也做相同的构造及接口注入。

  1. 代码移动

同样使用moduraize进行移动,代码移动至独立的fileBundle,加上对应的Gradle依赖:
图片[1]-移动应用遗留系统重构(7)- 解耦重构演示篇(一)+视频演示-一一网
移动后模块结构如下:

  1. 功能验证

执行冒烟测试,验证功能

./gradlew app:testDebug --tests SmokeTesting

复制代码

每一小步重构时都可以频繁运行测试,提前发现问题

代码演示:mp.weixin.qq.com/s/xbAIu6bWS…

具体的代码:github链接

总结

本篇我们按着重构的4个步骤,借助IDE的安全重构,小步的重构了library和file2个包。虽然依赖解除了,代码也移动到独立的模块里面。但是我们还是发现了一些问题。

  1. UserState接口的层层注入,我们需要手工维护了好多构造方法及对应的注入
  2. App依赖了fileBundle的Fragment,UI跳转上存在编译的依赖

下一篇,单体移动应用“模块化”演进之旅(8)- 依赖注入篇,我们将分享常见的注入方式及业内优秀的实践,并对DiskCloud继续进行改造优化。

CloudDisk示例代码

CloudDisk

系列链接

移动应用遗留系统重构(1)- 开篇

移动应用遗留系统重构(2)-架构篇

移动应用遗留系统重构(3)-示例篇

移动应用遗留系统重构(4)-分析篇

移动应用遗留系统重构(5)- 重构方法篇

移动应用遗留系统重构(6)- 测试篇

大纲

关于

欢迎关注CAC敏捷教练公众号。微信搜索:CAC敏捷教练

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享