云课堂Android模块化实战--如何设计一个通用性的模块
本文来自 网易云社区 。
如何设计一个通用性的模块
前言
每个开发者都会知道,随着项目的开发,会发现业务在不断壮大,产品线越来越丰富,而留给开发的时间却一直有限,在有限的时间,尽快完成某个功能的迭代。因此为了减少开发成本,保证业务功能复用,我们会将一些业务独立出来,比如直播间、消息等,做成单独的模块。所以想必都会都模块化开发有所了解。
本文的目的,并不是讲述如何处理模块化后的每个模块之间的通信问题,以及整个应用的架构问题,而是对于做了这么多模块后,对模块有个总结,在需要创建一个新的模块的时候,可以少走弯路,如何设计一个通用性的模块。
模块定义
独立的业务模块。
它和组件的区别是:
- 组件:指的是单一的功能组件,如地图组件、支付组件、路由组件、分享组件等等。
- 模块:指的是独立的业务模块,如直播间模块、消息模块、课程详情模块、课时学习模块等等。
模块可以依赖一些组件,模块与模块之间应该不要有依赖关系。
模块类图
在开始讲述模块的结构前,先看下完整的模块类图,分为内部元素,外部元素以及外部实现。 内部元素,只能在模块内部流动。 外部元素,在模块内部和外部之间流通的。 外部实现,这些类需要在模块外部定义。
领域驱动设计(DDD)的几个基本概念
在讲述模块内的元素之间,先了解几个DDD钟的基本概念:
- 实体 当一个对象由其标识(而不是属性)区分时,这种对象称为实体(Entity)。
- 值对象 当一个对象用于对事务进行描述而没有唯一标识时,它被称作值对象(Value Object)。
- 聚合根 Aggregate(聚合)是一组相关对象的集合,作为一个整体被外界访问,聚合根(Aggregate Root)是这个聚合的根节点。
- 领域服务 一些重要的领域行为或操作,可以归类为领域服务。它既不是实体,也不是值对象的范畴。
模块元素
- XXXModuleClient 向外提供服务的类,提供静态方法。
- ModuleInstance 整个模块的最重要的类,模块使用前需要初始化它。
- ModuleConfig 模块功能的配置项,是否需要打开某个功能等等。
- ModuleDependency 模块的一些依赖项,就是模块自身无法完成的功能,或者模块内并不关心这个功能。如对于直播间来说,它并不知道参加课程这些逻辑,这些逻辑应该是课程模块所关心的。
- LaunchData 针对每次服务可配置的选项。
- UI视图 如Box,它只关心自己的行为,以及用于向页面展示的数据。
- ActivityOfFragment 一些Box的组装者,负责将领域模型适配成视图模型,塞给Box。
- ILogic 所有的业务逻辑都发生在这里,它可以调用具体的服务完成业务逻辑,也可以交给领域模型实现。
- 领域模型 针对这个模块所抽象出来的数据模型,它不是简单get、set,还包含了业务逻辑。ILogic中可以引用住聚合根,其他的实体不应该被引用住。这样不对导致对象的引用散布在各处。
- DataSource 数据来源,可以从数据库,也可以从服务器。最终要转化成对应的领域模型。
- 服务对象 单个领域模型无法处理的逻辑一般会交给服务对象。
模块初始化
最开始能想到的便是,在应用启动的时候便对模块进行初始化,这样做的好处,简单、快捷。但是增加了应用启动耗费的时间,也增加了应用的内存。
更为通用常见的方式是,懒加载,在模块用到的时候在进行初始化。
因为模块的核心类是ModuleInstance,所有的服务都是靠它实现的。因此,模块初始化也就是这个单例对象的初始化。这样在需要模块服务的时候通过获取这个单例,如果单例未创建,则进行初始化即可。
在模块初始化的时候,我们将初始化的流程通过一个接口,暴露给第三方使用着,使其可以参与到模块初始化流程。
定义一个ModuleConfigAndDependency接口,提供两个回调方法。
public interface ModuleConfigAndDependency {
//自定义修改模块config配置
void applyConfig(Builder builder);
//自定义配置模块依赖
void applyDependency(Builder builder);
}
在调用ModuleInstance构造器之间,先创建一个Builder,通过解析一个双方约定好的配置文件,获取ModuleConfigAndDependency的实现类的名字,通过反射创建对象。将构建ModuleInstance的Builder传递出去,完成自定义配置。在Android中,约定的配置文件可以写在清单文件中。
模块的配置和依赖
模块的配置分为两种,一种是模块初始化完成后,配置就定了。另一种,是每次启动服务时的配置。两种的区别在于,作用域不同。一个针对全局的,一个针对每次服务。
全局的通过模块初始化的时候,修改Builder。
针对每次服务的,通过LaunchData配置。 在ModuleInstance中有一个存放LaunchData的Map。
依赖和配置类似,这里就不赘述了。
模块的领域模型
为什么需要自己的领域模型呢?
- 既然叫做领域,那它关注的重心就是自身模块的业务,然后我们可以将一些业务逻辑下沉到领域模型中。
- 与后端的隔离,有时候移动端与后端的开发可能是并行的,也可能移动端早于后端。那么接口的DTO会频繁的变动,移动端开发不得不去配合他们,影响自身的开发效率。如果移动端做好这层隔离,那么上层业务的开发完全不会受影响,只需要修改DataSource这层即可。
领域模型的构建来自DataSource,可以从数据库、也可以从服务器,最终的传递给上层的数据必须是领域模型。
模块的视图/View层
展现给用户的是由一块块区域组合而成的,我们将这些区域称之为Box,Box所关心的就只有两个,一个是用户行为,一个是视图模型。
- 对于行为,Box只是触发行为,比如点击,比如曝光,而具体的执行逻辑,交给外部实现。
- 对于视图模型,Box只是将其展现出来,如文字。
模块的控制层/Presenter
之前View与数据之间会有耦合性,现在交给Logic来解耦。Logic可以通过定义接口的形式调用View的变化,也可以通过Message的形式通知。而View通过ILogic定义的接口来获取需要的数据,以及逻辑。
模块的边界
每个模块都有自己的上下文,因而少不了模块与外部之间对象转换。 因此需要定义这样一层隔离机制,来完成模块内的上下文与模块外的上下文之间的流通。
可以是共享内核的形式,两个上下文依赖部分共享的模型。这种方式,模块可能要依赖一些通用Model模块。 可以是防腐层的形式,一个上下文通过一些适配和转换与另一个上下文交互。这种方式需要在模块内定义一些简单的值对象。
所以我们规定模块内的类访问权限,对于使用者,它所能访问的有以下:
- 暴露给使用者的服务接口
- 用于方法调用时的依赖对象类
总结
好的模块设计,应该是奔着可复用,高内聚,低耦合的方式。最重要的是灵活,可配置,易于扩展。
以上是最近一年开发,对于模块设计的理解及总结。如有不足不对的地方,请多多指点~
本文已由作者陈柏宁授权网易云社区发布,原文链接:云课堂Android模块化实战--如何设计一个通用性的模块
云课堂Android模块化实战--如何设计一个通用性的模块的更多相关文章
- Netty实战:设计一个IM框架
来源:逅弈逐码 bitchat 是一个基于 Netty 的 IM 即时通讯框架 项目地址:https://github.com/all4you/bitchat 快速开始 bitchat-example ...
- 如何在Visual Studio 2017中使用C# 7+语法 构建NetCore应用框架之实战篇(二):BitAdminCore框架定位及架构 构建NetCore应用框架之实战篇系列 构建NetCore应用框架之实战篇(一):什么是框架,如何设计一个框架 NetCore入门篇:(十二)在IIS中部署Net Core程序
如何在Visual Studio 2017中使用C# 7+语法 前言 之前不知看过哪位前辈的博文有点印象C# 7控制台开始支持执行异步方法,然后闲来无事,搞着,搞着没搞出来,然后就写了这篇博文,不 ...
- webview之如何设计一个优雅健壮的Android WebView?(下)(转)
转载:https://iluhcm.com/2018/02/27/design-an-elegant-and-powerful-android-webview-part-two/ (这篇文章写得有点晚 ...
- 如何设计一个优雅健壮的Android WebView?(下)
转:如何设计一个优雅健壮的Android WebView?(下) 前言 在上文<如何设计一个优雅健壮的Android WebView?(上)>中,笔者分析了国内WebView的现状,以及在 ...
- Android实训案例(九)——答题系统的思绪,自己设计一个题库的体验,一个思路清晰的答题软件制作过程
Android实训案例(九)--答题系统的思绪,自己设计一个题库的体验,一个思路清晰的答题软件制作过程 项目也是偷师的,决心研究一下数据库.所以写的还是很详细的,各位看官,耐着性子看完,实现结果不重要 ...
- webview之如何设计一个优雅健壮的Android WebView?(上)(转)
转接:https://iluhcm.com/2017/12/10/design-an-elegant-and-powerful-android-webview-part-one/ 前言 Android ...
- 如何设计一个优雅健壮的Android WebView?(上)
转:如何设计一个优雅健壮的Android WebView?(上) 前言 Android应用层的开发有几大模块,其中WebView是最重要的模块之一.网上能够搜索到的WebView资料可谓寥寥,Gith ...
- Android 设计一个菱形形状的Imageview组件.
网上没有资料,特来请教下大神 Android 设计一个菱形形状的Imageview组件. >> android这个答案描述的挺清楚的:http://www.goodpm.net/postr ...
- Android学习笔记(十二)——实战:制作一个聊天界面
//此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 运用简单的布局知识,我们可以来尝试制作一个聊天界面. 一.制作 Nine-Patch 图片 : Nine-Pa ...
随机推荐
- Shell32.ShellClass服务器操作系统无法获取 音频文件时长问题
前言: 上传音频文件,自动写入此音频文件的时长,这里用 COM组件Microsoft Shell Controls And Automation来实现. 首先 1.引用:Microsoft Shell ...
- margin+absolute布局:右栏固定主内容自适应 demo
margin+absolute布局:右栏固定主内容自适应 demo 头部 Aside侧边栏区域 Main主内容区域 底部 #demo{width:80%;margin:auto;height:300p ...
- IOS学习经验总结
学习ios有一段时间了,感觉有些盲目.有些迷茫,从网上收集了一些比人的学习经验,来参考. IOS学习经验总结--来自知乎网友 转自知乎:http://www.zhihu.com/question/20 ...
- dev GridControl显示标题
gridview:ShowViewCaption = TrueViewCation = "32435354354"
- c# ?和??
先看如下代码: string strParam = Request.Params["param"]; if ( strParam== null ) { strParam= ...
- Opencv3 图片膨胀与腐蚀
#include <iostream>#include <opencv2/opencv.hpp> using namespace std;using namespace cv; ...
- mybaties 一对多关系映射
背景: 数据库格式如下图所示 现在要统计出在一段时间内dimension_type为op即所有运营商的pv.uv.vv等指标的数组,以便页面显示出每个运营商在该事件段内历史指标曲线图. 分析: 返回的 ...
- 多校训练4——Hehe
递推题: dp[i]表示字符串第i个字母前有多少种不同的方法 1.出现一个hehe:dp[i]=dp[i-4]+dp[i-2] 意思是dp[i]=当前的hehe换成wqnmlgb+当前的hehe不换成 ...
- AJAX和DHTML
DHTML: (动态的html)本身不是一门新语言,而是一门新技术,包含以下 html . css . dom . js AJAX : 也是一门新技术包含 html . css. dom ...
- Html创建表单
echo Html::beginForm(['/site/logout'], 'post'); echo Html::submitButton(Yii::t('app', 'logout'), ['c ...