模块化开发思路

摘要

移动应用开发早期很少涉及一些复杂的大型项目和复杂项目需求(Android还有一部分原因eclipse开发IDE限制),然而随着移动应用发展迫使我们要对项目进行一些结构上的优化。组件化就是这样的一种优化。

个人感觉优点如下:

  1. 解放生产力,提高效率。

  2. 项目重用性和可维护性提高。

  3. 独立编译,独立测试,节约时间。

  4. 分工更明确细致,人员成本降低。

  5. 插拔性极高,结构清晰规范。

组件化和模块化

概念理解

简单来说,模块化就是将一个程序按照其功能做拆分,分成相互独立的模块,以便于每个模块只包含与其功能相关的内容。模块我们相对熟悉,比如登录功能可以是一个模块,搜索功能可以是一个模块。

模块化和组件化本质思想是一样的,都是“大化小”,两者的目的都是为了重用和解耦,只是叫法不一样.如果非要说区别,那么可以认为模块化粒度更小,更侧重于重用,而组件化粒度稍大于模块,更侧重于业务解耦。

模型区别

简单开发模型

不难发现,往往是在一个界面中存在着大量的业务逻辑,而业务逻辑中充斥着各种各种网络请求,数据操作等行为,整个项目中没有所谓的模块的概念,项目组成的基本单位不是模块,而是方法级的。

关于这种开发模型没什么需要介绍的,我们早期都经历过,现在除了很少非常古老的项目以及初学者练手之作,已经很少见到。

在这种模型上充其量最多也就是抽出几个公共的工具类,所以复用性极差。耦合性很高也导致维护和重构及其复杂。

单工程开发模型

该种开发模型已经有了明确的模块划分,并且通过逻辑上的分层呈现出较好结构,该模型最为我们所熟悉,通常用于早期产品的快速开发,项目规模和团队规模较小没有其他特殊需求的情况下采用。

组件化开发模型

借助组件化这一思想,我们在“单工程”模型的基础上,将业务层中的各业务抽取出来,封装成相应的业务组件,将基础库中各部分抽取出来,封装成基础组件,而主工程是一个可运行的app,作为各组件的入口(主工程也被称之为壳程序)。这些组件或以jar的形式呈现,或以aar的形式呈现。主工程通过依赖的方式使用组件所提供的功能。

多工程组件化开发模型

抽象及划分

组件化首要做的事情就是划分组件,如何划分并没有一个确切的标准。

划分组建的粒度不要太细,要根据实际业务变化而定,在业务逻辑独立的前提下尽可能的大一些。

图中绿色是基础组建部分,红色部分是业务组件部分,黄色是视图部分demo和anchor是两个可运行的app.

组件化的本质

组件化的本质其实就是降低耦合实现最大化分离,实现一种软件上的可插拔(就像电脑主板和显卡,当然插线越少越好)

降低耦合的手段:

  1. 暴漏接口而不是具体实现。
1
2
3
4
public interface ILoginView extends IView {

void loginSuccess();
}
  1. 避免依赖,可采用注入的方式,最好采用Java注解。

  2. 使用路由和数据总线。

驱动方式

UI驱动

UI驱动是我们最熟悉的开发流程,按照设计图实现界面,根据界面的交互和事件实现具体逻辑,根据逻辑需要实现数据模型。

数据模型驱动

数据模型驱动是在已经定义好了数据库模型的前提下采取的一种开发方式,前端开发很少见。

业务驱动

在实际情况中我们了解业务逻辑可能要比UI更早,所以如果在没有UI界面的情况下就可以去实现具体的业务逻辑这样就可以消除等待时间差。

业务逻辑驱动的核心是UI和Model接口的定义,难点也在于接口定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private AccountModelImpl mAccountModel;  //Model接口
private ILoginView mLoginView; //UI接口

public LoginPresenterImpl(){
mAccountModel = new AccountModelImpl();
}

@Override
public void requestAccountLogin(String account, String pwd) {
//TODO 业务逻辑实现(比如判断账号和密码是否合法)

mAccountModel.requestAccoutLogin(account, pwd, new Response.OnResult<UserInfo>() {
@Override
public void result(UserInfo response) {
//TODO 业务逻辑实现
mLoginView.loginSuccess();
}
});
}

模块化测试

我发现很多公司在实际开发中很大一部分时间花在了接口调试上面,个人感觉这个真的是很愚蠢,很没有必要,模块化可以帮我们大大提高业务逻辑和接口的测试效率,准确找到相关问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 接口测试
*
*/
public class PresenterTest{

static{
BaseInit.mJunitTest = true;
}

@Test
public void loginPresenterTest() throws Exception {
Assert.assertTrue(new LoginPresenterImpl().test());
}

@Test
public void accountPresenterTest() throws Exception{
Assert.assertTrue(new AccountPresenterImpl().test());
}
}

重用

重用我们这里考虑到组件之间的重用,而且还需要考虑接口的重用和UI的重用,组件的重用在于降低耦合,不再分析,我们来看看接口的重用。

接口重用

任何复用的前提条件都是遵循一定的规则,否则没法实现。我们的接口也一样。

1
2
3
4
5
6
7
8
9
10
11
**返回状态**
{status: 0, content: "xxxxxx"}

**返回对象**
{status: 0, content: "xxxxxx", data:{}}

**返回对象集合**
{status: 0, content: "xxxxxx", data:[{}, {}]}

**返回字符串集合**
{status: 0, content: "xxxxxx", data:["xxx", "aaa", "xxxxxx"]}

除了最基本的遵循定义规则,还需要约定一些业务划分和抽象规则。

例如:在我们的APP内有两处Banner, 一个在首页含有图片标题和链接。我们这样定义

1
2
3
4
5
public class Banner{
private String imgUrl;
private String title;
private String linkUrl;
}

而另一个地方的Banner在社区,有app内跳转类型和播放时长,没有链接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Banner{
private String imgUrl;
private String title;
private int type;
private long time;
}

根据业务需求上面的接口定义没有任何问题,但是如果我们这样定义就相当于从界面驱动了,而没有从业务逻辑驱动。

如果从业务逻辑思考这个Banner,它就是一个展示广告,和界面的位置无关也不分类型,应该划分为公共接口。

```java
public class Banner{
private String imgUrl;
private String title;
private String linkUrl;
private int type;
private long time;
}

这样定义就可以在任意地方调用同一个Banner接口,因为这件事情本身就和界面位置无关。

这是一个最简单的例子,实际类似的地方很多,重要的是思维的转变。

UI重用

UI的重用分为两方面:

1.是为了实现多个APP共用逻辑组件。

实现共用逻辑组件的前提是业务逻辑的可重用性,只要界面的设计不要产生业务逻辑实现上的冲突一般不会出现问题。

2.是多个APP之间公共部分的重用。

我们在实现app差异化的同时其实要遵守一些Google或者Apple的设计规范,或者一些用户约定的行为规范,这样就可以达到设计公共部分的重用。

评论

Ajax Android AndroidStudio Animation Anroid Studio AppBarLayout Babel Banner Buffer Bulma ByteBuffer C++ C11 C89 C99 CDN CMYK COM1 COM2 CSS Camera Raw, 直方图 Chrome Class ContentProvider CoordinatorLayout C语言 DML DOM Dagger Dagger2 Darktable Demo Document DownloadManage ES2015 ESLint Element Error Exception Extensions File FileProvider Flow Fresco GCC Git GitHub GitLab Gradle Groovy HTML5 Handler HandlerThread Hexo Hybrid I/O IDEA IO ImageMagick IntelliJ Intellij Interpolator JCenter JNI JS Java JavaScript JsBridge Kotlin Lab Lambda Lifecycle Lint Linux Looper MQTT MVC MVP Maven MessageQueue Modbus Momentum MySQL NDK NIO NexT Next Nodejs ObjectAnimator Oracle VM Permission PhotoShop Physics Python RGB RS-232 RTU Remote-SSH Retrofit Runnable RxAndroid RxJava SE0 SSH Spring SpringBoot Statubar Style Task Theme Thread Tkinter UI UIKit UML VM virtualBox VS Code VUE ValueAnimator ViewPropertyAnimator Vue Vue.js Web Web前端 Workbench api apk bookmark by关键字 cli compileOnly component computed css c语言 databases demo hexo hotfix html iOS icarus implementation init jQuery javascript launchModel logo merge methods mvp offset photos pug query rxjava2 scss servlet shell svg tkinter tomcat transition unicode utf-8 vector virtual box vscode watch webpack 七牛 下载 中介者模式 串口 临潼石榴 主题 书签 事件 享元模式 仓库 代理模式 位运算 依赖注入 修改,tables 光和色 内存 内核 内部分享 函数 函数式编程 分支 分析 创建 删除 动画 单例模式 压缩图片 发布 可空性 合并 同向性 后期 启动模式 命令 命令模式 响应式 响应式编程 图层 图床 图片压缩 图片处理 图片轮播 地球 域名 基础 增加 备忘录模式 外观模式 多线程 大爆炸 天气APP 太白山 头文件 奇点 字符串 字符集 存储引擎 宇宙 宏定义 实践 属性 属性动画 岐山擀面皮 岐山肉臊子 岐山香醋 工具 工厂模式 年终总结 开发技巧 异常 弱引用 恒星 打包 技巧 指令 指针 插件 插值 摄影 操作系统 攻略 故事 数据库 数据类型 数组 文件 新功能 旅行 旋转木马 时序图 时空 时间简史 曲线 杂谈 权限 枚举 架构 查询 标准库 标签选择器 样式 核心 框架 案例 桥接模式 检测工具 模块化 模板 模板引擎 模板方法模式 油泼辣子 泛型 洛川苹果 浅色状态栏 渲染 源码 源码分析 瀑布流 热修复 版本 版本控制 状态栏 状态模式 生活 留言板 相册 相对论 眉县猕猴桃 知识点 码云 磁盘 科学 笔记 策略模式 类图 系统,发行版, GNU 索引 组件 组合模式 绑定 结构 结构体 编码 网易云信 网格布局 网站广播 网站通知 网络 美化 联合 脚手架 膨胀的宇宙 自定义 自定义View 自定义插件 蒙版 虚拟 虚拟机 补码 补齐 表单 表达式 装饰模式 西安 观察者模式 规范 视图 视频 解耦器模式 设计 设计原则 设计模式 访问者模式 语法 责任链模式 贪吃蛇 转换 软件工程 软引用 运算符 迭代子模式 适配器模式 选择器 通信 通道 配置 链表 锐化 错误 键盘 闭包 降噪 陕西地方特产 面向对象 项目优化 项目构建 黑洞
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×