基于Github APP的软件设计方案分析
这篇博客主要针对我个人的工程实践项目,进行软件系统分析和设计,最终形成软件系统概念原型。
一、项目设计方案
本项目是基于Java和flutter开发一个面向github用户的安卓应用程序,可以给用户提供github.com网页上能够提供的常用功能。包括实现登录和登出、查看用户信息、关注/取关用户、查看版本库信息、星标、克隆和关注版本库、查看通知和问题,搜索信息(包括版本库和用户),设置软件信息等等。
- 使用flutter和native的混合开发模式。
- 使用层次化作为软件架构的风格与策略,从垂直纵深的角度将软件单元按层次化组织,每一层为它的上一层提供服务,同时又作为下一层的客户,分为业务层、通用UI组件层、通用基础功能层,基础第三方lib层。
- 在业务层中使用MVP作为软件的架构模式。
- 使用GraphQL以及Restful向restful请求数据,使用多种框架,包括OKhttp、Glide、Flutter-boot、Flutter-boost、Arouter、Butterknife等优秀的开源框架。例如,使用OKhttp作为处理网络请求的框架,Glide作为图片缓存的框架等等。
- 完成基本的代码实现之后,对app进行全方位的优化,包括使用leakCanany进行内存泄露检测、使用blockCanary进行主线程卡段检测、进行内存优化、进行布局优化,防止过度渲染等等。
1. 设计模式
设计模式的本质是面向对象设计原则的实际运用总结出的经验模型。
正确使用设计模式具有以下优点。
• 可以提高程序员的思维能力、编程能力和设计能力。
• 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
• 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
在工程实践项目中,我们使用了单例模式、装饰模式、责任链模式等等。下面分别举例:
单例模式(Instance)
某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,典型的应用如数据库实例。
例如:
本项目中我们使用Glide作为图片缓存框架,而Glide使用的是单例模式。
使用这种单例模式的优点是既能够在需要时才初始化单例,又能够保证线程安全,且单例对象初始化后调用get不进行同步锁。通过 synchronized 关键字来实现同步块,所有加上synchronized和块语句,在多线程访问的时候,同一时刻只能有一个线程能够用。
装饰模式(Decorator)
在不改变现有对象结构的情况下,动态地给对象增加一些职责,即增加其额外的功能。装饰模式实质上是用对象组合的方式扩展功能,因为比继承的方式扩展功能耦合度低。
例如: 本项目中的Context类,实际上是一个抽象类,是一个应用程序环境信息的接口。里面定义了各种抽象方法,包括获取系统资源、获取系统服务,发送广播,启动Activity,Service等。
它采用了装饰模式:

Context的关联类(直接子类)有:ContextWrapper(Context的封装类) ContextImpl(Context的实现类),接下来的类都是继承自ContextWrapper这个装饰类的具体装饰类,包括ContextThemeWrapper、 Application和Service。换句话说,Context类是最根部的抽象类,不实现具体的功能。Context类的具体实现实际上是在ContextImpl类,里面具体实现了startActivity()方法。Activity、Service都是继承自ContextWrapper,ContextWrapper持有ContextImpl的引用,是其余装饰类的基类。
责任链模式
采用责任链模式,意思是用来处理相关事务责任的一条执行链,链上拥有若干节点,如果某个节点处理完了就可以根据实际业务需求传递给下一个节点继续处理或者返回处理完毕。
例如:
本项目中使用OKHttp作为处理网络请求的框架:一共有五个拦截器,分别为:
- 重试拦截器 (RetryAndFollowUpInterceptor)
- 基础的拦截器(BridgeInterceptor)
- 缓存拦截器 (CacheInterceptor)
- 连接的拦截器(ConnectInterceptor)
- CallServerInterceptor
其工作流程如下图所示:

2. 软件架构模式
本项目采用MVP作为软件架构模式,它基于MVC,架构图如下所示:

- M(Model) 数据相关层
- V(View) 视图层,如Activity上的布局
- P(Presenter) 纽带层,用来连接Model与View.
MVP开发在Android中的基本流程:
- View层定义View.interface,用来定义View的行为。一般由Activity或者是Fragment来实现这个接口,它定义了View视图的各种变化,如设置Textview,加载对话框,更新进度条等。
- Model层定义Model.interface,这个是用来定义数据层发生变化时的通知接口,因为Model不能直接与View交互,所以它与Presenter交互,然后再通过Presenter间接达到与View的交互。
- Presenter翻译的意思是主持人,也就是主持场合,控制节奏的意思。在这时Presenter就负责具体的业务逻辑,请求数据,把数据送到Model,或者监听Model的数据变化,接受View层的动作,负责通过通知View层的视图变化。
MVP的优点包括:代码解耦、结构清晰、可复用、扩展性高、方便进行单元测试。
在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。而且,Presenter与具体的View是没有直接关联的,而是通过接口进行交互,从而使得在变更View时候可以保持Presenter的不变,可以多次复用。
MVP主要解决的问题是把逻辑层抽出来成P层,要是遇到需求逻辑上的更改就可以只需要修改P层了或者遇到逻辑上的大改我们可以直接重写一个P也可以,很多开发人员把所有的东西都写在Activity/Fragment里面,这样一来遇到频繁改需求或者逻辑越来越复杂的时候,Activity /Fragment里面就会出现过多的混杂逻辑导致出错,所以MVP模式对于APP来对控制逻辑和UI的解耦来说是一个不错的选择。
大型软件系统的软件架构模型在整个项目中起到至关重要的作用。
首先,软件架构模型有助于项目成员从整体上理解整个系统;
其次,给复用提供了一个高层视图,既可以辅助决定从其他系统中复用设计或组件,也给我们构建的软件架构模型未来的复用提供了更多可能性;
再次,软件架构模型为整个项目的构建过程提供了一个蓝图,贯穿于整个项目的生命周期;
最后,软件架构模型有助于理清系统演化的内在逻辑、有助于跟踪分析软件架构上的依赖关系、有助于项目管理决策和项目风险管理等。
3. 软件架构的风格与策略
本项目采用层次化作为软件架构的风格与策略。
较为复杂的系统中的软件单元,仅仅从平面展开的角度进行模块化分解是不够的,还需要从垂直纵深的角度将软件单元按层次化组织,每一层为它的上一层提供服务,同时又作为下一层的客户。
层次结构如下:
业务层(biz):以功能页面为单位,例如:首页、个人页、版本库等
通用UI组件层(common UI):例如页面顶部的导航栏、通用Dialog、通用网络图片控件等
通用基础功能层(common): 纯功能,跟界面没有联系,例如:路由、网络、图片等
基础第三方lib(thirdparty):okhttp、Glide、RxJava、Dagger、ButterKnife等第三方库
层次结构图如下所示:
后来为了提取出业务层中通用的业务逻辑,我们添加了通用业务层,包括通用的业务逻辑代码,例如设置、登陆等。

二、视图
我们可以采用不同的视图来描述项目的软件系统概念原型,比如分解视图、依赖视图、泛化视图、执行视图、实现视图、部署视图、工作分配视图等。
2.1 分解视图
分解是构建软件架构模型的关键步骤,分解视图也是描述软件架构模型的关键视图,一般分解视图呈现为较为明晰的分解结构(breakdown structure)特点。分解视图用软件模块勾画出系统结构,往往会通过不同抽象层级的软件模块形成层次化的结构。组件主要有类、软件模块、软件单元、子系统等。

2.2 依赖视图
依赖视图展现了软件模块之间的依赖关系。比如一个软件模块A调用了另一个软件模块B,那么我们说软件模块A直接依赖软件模块B。如果一个软件模块依赖另一个软件模块产生的数据,那么这两个软件模块也具有一定的依赖关系。依赖视图在项目计划中有比较典型的应用。比如它能帮助我们找到没有依赖关系的软件模块或子系统,以便独立开发和测试,同时进一步根据依赖关系确定开发和测试软件模块的先后次序。依赖视图在项目的变更和维护中也很有价值,比如它能有效帮助我们理清一个软件模块的变更对其他软件模块带来影响范围。
依赖是一种使用的关系,即一个类的实现需要另一个类的协助,所以要尽量不使用双向的互相依赖。在代码上主要表现为局部变量、方法的参数或者对静态方法的调用。
首先分析模块之间的依赖关系,首先交互子系统以及查看信息子系统下方的所有模块都是依赖于登入登出模块的,而登入登出模块依赖于注册子系统,用户交互模块和版本库交互模块分别依赖于用户信息模块和版本库信息模块。
得到依赖视图如下所示:

2.3 泛化视图
泛化视图展现了软件模块之间的一般化或具体化的关系,典型的例子就是面向对象分析和设计方法中类之间的继承关系。值得注意的是,采用对象组合替代继承关系,并不会改变类之间的泛化特征。因此泛化是指软件模块之间的一般化或具体化的关系,不能局限于继承概念的应用。
泛化视图有助于描述软件的抽象层次,从而便于软件的扩展和维护。比如通过对象组合或继承很容易形成新的软件模块与原有的软件架构兼容。
三、项目的数据库设计
user :
| 字段名称 | 字段类型 | 字段描述 |
|---|---|---|
| name | String | 用户昵称 |
| login | String | 用户登录名 |
| regTime | Date | 注册此账号的时间 |
| activity | ArrayList[Activity] | 该用户产生的所有活动 |
| trending | ArrayList[Trending] | 当前github的所有趋势版本库信息 |
repository :
| 字段名称 | 字段类型 | 字段描述 |
|---|---|---|
| name | String | 包含持有者名称在内的仓库名 |
| description | String | 关于此项目的描述信息 |
| createTime | String | 项目的创建时间 |
| mLanguage | String | 项目中使用比例最大的语言名称 |
| size | String | 此项目的大小 |
enroll:
| 字段名称 | 字段类型 | 字段描述 |
|---|---|---|
| option | String | 用户对版本库采取的操作类型 |
| owner | Boolean | 当前用户是否持有该版本库 |
| forkFlag | Boolean | 当前用户是否克隆了此版本库 |
| watchFlag | Boolean | 当前用户是否关注了此版本库 |
| starFlag | Boolean | 当前用户是否星标了此版本库 |
activity:
| 字段名称 | 字段类型 | 字段描述 |
|---|---|---|
| name | String | 活动的执行者 |
| avataUrl | String | 活动执行者的头像图片地址 |
| time | Date | 活动发生的时间 |
| action | String | 活动的类型:如push star等等 |
| message | String | 活动的具体内容 |
trending:
| 字段名称 | 字段类型 | 字段描述 |
|---|---|---|
| name | String | 版本库名称 |
| trendingTime | Date | 版本库所处的趋势时间范围 |
| chosnLan | String | 版本库的语言 |
四、项目的实现视图
整体上是一个利用flutter-boost插件来实现Android的原生开发和Flutter开发混合在一起的项目, 首先分成了一个层次,如下图所示:

其中app采用MVP模式:

五、软件系统运行环境和技术选型说明
软件系统运行环境:

该软件运行在Android 11.0 API30的系统上
技术选型说明:
-
针对图片加载框架,我们针对市面上常见的几种加载框架,包括Universal Image Loader、Picasso、Glide、Fresco进行了全方位的比较。
Universal Image Loader是早期比较有代表性的图片加载库,虽然目前已经停止维护,不再推荐使用,但是其架构设计和实现依然值得借鉴。Picasso的设计充分体现了Square公司在架构设计上一贯的简洁易用风格(链式调用)。Glide充分吸收了Picasso的优点,并在此基础上做了大量的优化和改进。Fresco 可以说是综合了之前图片加载库的优点并将性能优化到极致,但它的包很大,用法比较复杂,API不够简洁。所以,综合考虑,本次开发采用glide作为图片的加载框架。
-
针对路由框架,我们学习了当前流行的几种路由框架几种:包括Router、Rudolph、ARouter。
经过综合考虑框架易用性、稳定性、文档资料丰富程度。最后决定的路由技术选型为ARouter。ARouter有强大的开发团队,有完整的技术文档,有丰富的项目示范,有广大的用户群体,是被广泛检验的优秀的开源框架,适合集成到项目中,因此决定采用ARouter作为路由框架。
-
针对架构模式,我们比较了MVC, MVVM和MVP,
首先,MVVM的双向绑定技术和数据状态管理是它的技术亮点,但这是把双刃剑,带来的问题如下:View和ViewModel之间界限不清,对初学者而言写出结构清晰的代码较难;绑定带来的就是View不够灵活,复用性不高;而且绑定使得bug不易调试。我们项目对View和Model数据更新的需求不高,而且希望能写出结构规范、清晰的代码,所以我们不选择MVVM。
其次,Android开发天然的就是MVC结构,Activity对应了MVC中的V和C,随着项目的深入,Activity处理的东西越来越多,代码也越来越臃肿,而且在修改需求时,因为V和C的耦合性,即使是一个小小的View的改变,也要在Activity臃肿的代码中找上半天,这对于程序员而言是件非常痛苦的事。
为了解决这种不必要的痛苦,提高开发效率,MVP把逻辑层抽出来成P层,Activity只充当V的角色,业务逻辑控制交给了Presenter。要是遇到需求逻辑上的更改就可以只需要修改P层了或者遇到逻辑上的大改我们可以直接重写一个P也可以,所以MVP模式对于APP来对控制逻辑和UI的解耦来说是一个不错的选择,所以我们最终选择的是MVP架构模式。
六、工作机制说明
系统中最核心的部分是,通过flutter-boost来实现native和flutter之间的通信。
Flutter Boost 中负责通信的是 BoostChannel,它的本质上是 MethodChannel 的一层封装,而 MethodChannel 是 Native 与 Flutter 通信的方案之一。

用户向登录系统输入用户名和密码,登录系统经过处理之后选择允许或不允许用户登录。用户登录后,可以向系统输入条件和数据,系统经过分析和筛选得到版本库、用户、其他等多种信息,版本库和用户之间为关联关系,大部分信息类和用户或版本库之间都为聚合关系,系统经过信息处理之后,会把中间结果转化为组中结果返回给用户,用户收到了满意的结果,这就是一次业务过程。
或者是,用户在登录之后,可以针对版本库进行操作,版本库被星标、克隆或关注,经过系统的处理之后,会把结果返回给版本库持有者,持有者收到信息,这也是一次业务过程。
再比如,用户在登陆之后,可以对用户进行操作,关注某个用户或者是取消关注某个用户,经过系统处理之后,会把结果返回给被关注或者是被取消关注的用户,用户收到信息。
七、总结
此博客主要根据个人的工程实践项目,进行了软件系统分析和设计,最终形成软件系统概念原型。
总结了项目设计方案中所蕴含的软件结构特点,比如设计模式、软件架构风格与策略等;并采用不同的视图来描述项目的软件系统概念原型,比如分解视图、依赖视图、泛化视图、执行视图、实现视图、部署视图、工作分配视图等。
参考资料: https://gitee.com/mengning997/se/tree/master/ppt

浙公网安备 33010602011771号