App架构经验总结(二)

原文链接:http://keeganlee.me/post/architecture/20160303 版权声明:本文刊载在《程序员》杂志2016年3期,版权归《程序员》所有,未经许可不得转载

架构分层

API的设计完成之后,接下来我就会考虑App项目的整体架构了。整体如何架构,我也曾经做过不少尝试。早期的时候,Android就是将所有操作都放在Activity里完成,包括界面数据处理、业务逻辑处理、调用API。后来发现Activity越来越臃肿,代码越来越复杂,很难维护。于是就开始思考如何拆分,如何才能做到松耦合高内聚。

前面也说过,一个App的核心就是数据,那么,从App对数据处理的角色划分出发,最简单的划分就是:数据管理、数据加工、数据展示。相应的也就有了三层架构:数据层、业务层、展示层。它们之间的关系如下图,数据层是三层中的最底层,往下,它接入API;往上,它向业务层交付数据。业务层夹在三层中间,属于数据的加工厂,将数据层提供上来的数据加工成展示层需要展示的数据。展示层处于三层中的最上层,主要就是将从业务层取得的数据展示到界面上。

数据层

数据层是数据管理者,主要任务就是封装API,并将数据结果交付给上层,中间会再加个数据缓存。整个主流程如下图:

  1. 业务层向数据层请求数据;
  2. 数据层检查缓存中有没有请求需要的数据;
  3. 如果有缓存数据,则直接返回缓存数据;
  4. 如果没有缓存数据,则从网络API获取数据,并将数据加入缓存,然后返回数据。

调用网络API时,还要判断网络状态,根据不同状态做不同处理。如果网络不可用,就无需发起请求了。网络可用时,也要区分是连接WIFI还是连接移动网络。连接移动网络时,一般需要限制调用比较耗流量的请求。曾经,我们没有对移动网络状态下的请求进行限制,结果,测试时流量DuangDuangDuang地一下子就不见了十几M。连接WIFI时,则无需设置这种限制,而且还可以预先请求一些接口,比如请求当前分页数据时,可以将下一页的数据也预先请求。

缓存也需要缓存策略,不同的接口需要做不同的缓存处理。首先,缓存只适用于获取数据的接口,对于修改数据的接口则不适用。其次,不同接口缓存时间一般也不同,对于很少变动的数据缓存时间可以设置长一些,而频繁变动的数据缓存时间则比较短,甚至不进行缓存。最后,缓存数据因为比较多,我们一般保存在数据库,而对于调用频率高、最新的数据,还会在内存中也拥有一份缓存,不过缓存时间比较短。请求缓存数据时,会先检查内存缓存中有没有,有则直接将缓存的数据返回,没有才从数据库获取。

那么,如何将数据交付给业务层呢?这是整个数据层模块与外部交互的部分,当与外部交互的时候,一般都要符合面向接口编程的原则,因此只要提供开放的数据接口就可以了。对于接口的参数需要说明一下,上面提到的参数有appKey、version、currentPage这几个,还有签名sign、时间戳time,其实可以分为两类:系统参数和业务参数。像appKey、version、sign、time这些属于系统参数,而currentPage,或username之类的则属于业务参数。数据层开放的数据接口的参数只需要包含业务参数就可以了,业务层并不需要关心系统参数是什么,系统参数在数据层内部封装API时指定就可以了。

业务层

业务层是数据加工者,主要就是从数据层获取数据,然后经过业务逻辑处理后转化成展示层需要的数据。业务层因为夹在数据层和展示层中间,起着承上启下的作用。也因此,业务层很容易沦落为只是一个数据的中转站,主要就是因为对业务层具体的作用和职责没有理解清楚。

这里用一个例子来说明业务层具体的工作吧,就举个用户注册的例子。用户注册时,界面上需要用户提供手机号、短信验证码、密码、确认密码。那么,最简单的操作就是,带上这些参数调用数据层的注册接口。好了,问题来了,注册接口并没有提供确认密码的参数。那好,调用注册接口之前先判断下密码和确认密码是否一致,不一致则返回错误提示给用户,一致了才调用注册接口。好了,第二个问题来了,用户等网络请求等了一段时间后,请求结果返回说手机号少了一位。下一次,又等了一段时间,这次又返回说手机号多了一位。就因为一个小错误要让用户等那么久,用户肯定有意见。后台也有意见,各种非法的请求都发过来,是嫌服务器压力不够大啊。那好,调用接口之前对这些参数做有效性检查吧,手机号要规范,短信验证码只能为六位数字,密码不能少于六位。终于注册成功了,第三个问题又来了,注册接口是没有返回用户的accessToken的,只有登录接口才会返回。让用户手动再登录一下?这用户体验不太好啊。正确的姿势应该是注册成功后再自动调用一次登录接口,如果因为网络问题第一次登录失败,后面还需要再自动调用多一次,如果还是调用失败,才让用户手动登录。

上面的例子中,对参数的有效性检查,注册成功后的自动登录,都属于业务逻辑的处理,也就是说都是业务层的工作。

业务层交付给展示层的数据也是通过接口的方式,不过,和数据层交付给业务层时不同的是:交付给展示层的数据应该是通过异步回调返回的。因为获取数据是一个比较耗时的任务,通过异步回调才不会阻塞UI主线程。

展示层

展示层作为数据展示者,它只要关心数据如何展示就可以了。不过,数据如何展示却不是那么简单。展示层是三层架构中最复杂的一层了,要考虑的东西远远多于其他两层,涉及的东西包括但不限于界面布局、屏幕适配、图片资源、文本资源、颜色资源等等。在开发一段时间后,展示层出现代码混乱是最常见的。因此,做好展示层,就需要保持高质量的代码。要保持高质量代码,我觉得至少应该遵循几条基本的原则:

  1. 保持规范性:定义好开发规范,包括书写规范、命名规范、注释规范等,并按照规范严格执行;
  2. 保持单一性:布局就只做布局,内容就只做内容,各自分离好,每个方法、每个类,也只做一件事情;
  3. 保持简洁性:保持代码和结构的简洁,每个方法,每个类,每个包,每个文件,都不要塞太多代码或资源,感觉多了就应该拆分。

所谓无规矩不成方圆,展示层的设计,要从开发规范开始。一份好的开发规范,是保证代码有较高的可读性的基础。iOS方面,苹果已经有一套Coding Guidelines,主要属于命名方面的规范。当我们制定自己的开发规范时,首先就要遵守苹果的这份规范,在此基础上再加上自己的规范。Android方面,我也在我的博客中分享过一套(Android技术积累:开发规范),主要分为书写规范、命名规范、注释规范三部分。

最重要的不是开发规范的制定,而是开发规范的执行。如果没有按照开发规范去执行,那开发规范就等于形同虚设,那代码混乱的问题依然得不到解决。

说到单一性,面向对象设计中,有一个基本原则就是单一职责原则,它规定一个类应该只有一个发生变化的原因。保持单一性是减低耦合度的关键标准,其目的就是各方面的解耦。而我这里说的单一性不只是规定类的单一,也包括界面的单一、方法的单一、资源文件的单一等。

界面的单一,首先是界面的布局和界面的数据应该分离。另外,界面数据的获取和展示也应该分离。一句话,保持界面的单一性就是要保持界面上每个维度都做好分离,从界面的布局,到数据的获取,数据的检查,数据的展示。

方法的单一,则表现为一个方法是对一个行为的封装。行为又可以拆分为多个步骤,每个步骤其实也是更细化的行为。因此,方法嵌套方法是一种常态。那么,保持方法的单一性,关键不在于怎么定义这个方法的行为,而在于这个行为要怎么拆分成更细的行为。举个例子,通常在Activity的onCreate方法,做初始化操作,细分出来就分为了:控件的初始化、逻辑变量的初始化、数据的初始化。数据的初始化又可以再细分:数据的获取、数据的展示。每个细化的行为都应该封装为一个独立的方法,这样,才真正符合方法的单一性。

资源文件的单一,主要是指Android的各类资源文件,包括存放字符串的strings.xml,存放字符串数组的arrays.xml,存放颜色值的colors.xml,存放尺寸值的dimens.xml,等等。资源文件的单一,是说所有相关的资源信息要在资源文件里定义并引用到代码或布局文件里,而不是在代码或布局文件里直接定义。这样做,可以很方便地做各种适配和修改,比如支持国际化,比如不同分辨率的屏幕用不同尺寸值。iOS则没有提供和Android一样的资源文件分离的机制,但可以参考Android的做法自己去实现。

原文发布于微信公众号 - Keegan小钢(keeganlee_me)

原文发表时间:2016-03-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android-JessYan

MVPArms官方快速组件化方案开源,来自5K star的信赖

原文地址: https://www.jianshu.com/p/f671dd76868f

1101
来自专栏吴生的专栏

水平分库分表的关键问题及解决思路

关系型数据库本身比较容易成为系统性能瓶颈,单机存储容量、连接数、处理能力等都很有限,数据库本身的“有状态性”导致了它并不像Web和应用服务器那么容易扩展。在互联...

48011
来自专栏BestSDK

一款完美的SDK产品,肯定具备这9个特质

即保证用户能够在5分钟以内学会使用代码。这一点非常重要,特别是考虑到有时候用户会评估我们的产品——如果无法轻松上手,他们很可能直接选择放弃。 ? 1. 简单性 ...

44510
来自专栏Bug生活2048

Ghost博客评论、统计、打赏接入

首先是选择问题,对于后端的我来说,在ghost基础赏进行二次开发显然有点难度,只能选择第三方插件来丰富自己的代码。

4383
来自专栏禁心尽力

基于服务的SOA架构_后续篇

昨天简单介绍了一下本人在近期开发过的一个电商购物平台的架构流程和一些技术说明;今天将详细总结一下在项目中用到的各个架构技术的环境部署和细节,希望能够帮到大家,如...

21710
来自专栏编程微刊

微信小游戏跳一跳外挂教程(安卓版)

3342
来自专栏web前端教室

前几天有个同学问我,“什么是响应式编程”?另,它和函数式编程有啥区别?

前几天有个同学问我,啥叫响应式编程?当时我正在讲课没顾得上回他。今天晚上仔细写个文章回复他,顺便我自己也学习一下。 响应式编程的英文名,Reactive Pro...

2606
来自专栏架构师之路

选redis还是memcache,源码怎么说?

memcache和redis是互联网分层架构中,最常用的KV缓存。不少同学在选型的时候会纠结,到底是选择memcache还是redis。

2064
来自专栏iOS Developer

Biosn收集的Git常见的错误解决方式--转自Bison的技术博客

1462
来自专栏smartguys

(七):C++分布式实时应用框架 2.0

版权声明:本文版权及所用技术归属smartguys团队所有,对于抄袭,非经同意转载等行为保留法律追究的权利!

1843

扫码关注云+社区

领取腾讯云代金券