关于Android 架构: 应用整洁架构
来源:Android架构师丨小熊     阅读:496
云上智慧
发布于 2019-08-22 01:30
查看主页

首先,我会移除在 Android 项目中不使用的东西,增加少量在 Uncle
Bob 的图中找不到的但我们需要使用的东西,看起来像这样:

我会从中心(笼统)讲到边缘(具体)。

Entities

实体(又称领域对象或者业务对象)是 app 的核心。它们代表 app 的主要功能,你应该能够仅通过查看实体来说出 app 是做什么的。它们包含业务逻辑 —— 仅限于验证和相似的东西。它们不和真实的外部世界交互,也不会解决持久化。假如你有一个新闻 app,那么实体就是类别、文章和商业。

Use Cases

用例,也称为 interactor(交互器),也可以叫做 business service (业务服务对象),是实体的扩展,是 business logic(业务逻辑)的扩展。也就是说,它们包含的业务逻辑不限于一个实体,而是可以解决很多实体。一个好用例的标准是,你可以使用一句简单的常用语言来形容它是做什么的,例如,“把钱从一个账户转移到另一个账户”。你甚至可以使用这样的命名系统来命名该类,例如 TransferMoneyUseCase。

Repositories

仓库用于持久化实体。就这么简单。它们定义为接口,并且用在要对实体执行增删查改操作的用例的输出端口。此外,它们可以公开少量与持久化相关的复杂操作,譬如过滤、聚合等。具体持久化策略,譬如数据库或者网络,在外层中实现。例如,你可以把接口命名为 AccountRepository。

Presenters

假如你熟习 MVP 模式,Presenter 做你想让它做的事情。它们解决客户交互,调用恰当的业务逻辑,并将数据发送给 UI 渲染。这里通常有各种类型的模型之间的映射转换。有些人会在这里使用控制器,这是可以的。我们使用的 Presenter 被正式称为监督控制器,我们通常根据屏幕方向为每个界面定义一个或者两个 Presenter,并且 Presenter 的生命周期和相关的 View 的生命周期绑定。一个建议:尝试以技术无关的方式命名 Presenter 中的方法,伪装你不知道 View 是用什么技术实现的。所以,假如在 View 中有方法名为 onSubmitOrderButtonClicked 和 onUserListItemSelected,那么解决这些事件的相应的 Presenter 中的方法可以被命名为 submitOrder 和 selectUser。

Device

这个组件在先前通知那个例子中已经被玩坏了。它包含了诸如传感器、闹钟、通知、播放器、各种 *Manager 等等真实 Android 功能的实现。它包含两部分组件。第一部分是定义在内层的接口,业务逻辑用它来作为和外部世界通信的输出端口。第二部分,也画在图中,是那些接口的实现。因而,比方,你可以定义名为 Gyroscope, Alarm, Notifications, 和 Player 的接口。请注意,这些名称是笼统的技术无关的。业务逻辑不关心通知如何显示,播放器如何播放声音,或者螺旋仪的数据来自哪里。你可以创立一个将通知写入终端,将声音数据写到日志,或者者从预先定义好的文件中收集螺旋仪数据的实现。这样的实现对于调试或者创立一个用于你编码确实定性的环境是很有用的。当然,你必需创立诸如 AndroidAlarm,NativePlayer 等等的实现。在大多数情况下,这些实现仅仅是 Android Manager 类的包装。

DB & API

这里没有哲学。将仓库的实现放在此组件中。所有的底层持久化的东西应该放在这里:DAO,ORM,Retrofit(或者别的),JSON 解析等等。你还可以在这里实现缓存策略或者者简单地在内存中(in-memory)持久化,直到你完成了 app 的其他部分。我们团队最近进行了一个有趣的探讨。问题是这样:仓库能否应该公开诸如 fetchUsersOffline(fetchUsersFromCache)和 fetchUsersOnline(fetchUsersFromInternet)之类的方法?换句话说,业务逻辑能否应该知道数据来自哪里。读完这篇文章的所有内容后,答案很简单:不。但这里有个圈套。假如关于数据源的决策是业务逻辑的一部分 —— 譬如,客户可以选择或者者 app 有一个明确的离线模式 —— 而后你可以增加这样的区分。但我不会为每个请求定义两种方法。我可能会在仓库中公开 enterOfflineMode 和 exitOfflineMode 这样的方法。或者者假如它适用于所有仓库,我们可以使用 enter 和 exit 方法定义一个 OfflineMode 接口,并在业务端使用它,让仓库去查询它的模式并且在内部决策。

UI

这里的哲学更少。将和 Android UI 相关的东西放在这里。Activity、Fragment、View、Adapter 等等。完了。

板块(Modules)

下图显示了我们如何将所有这些组件分解成 Android Studio 板块。你可能会发现另一种更合适的分法。

我们将实体、用例、仓库和设施接口分到领域板块。假如你想要一个额外的挑战(奖励是永恒的荣耀和完全整洁的设计),你可以使该板块成为一个纯 java 板块。这将阻止你走捷径将少量 Android 相关的东西放在这里。

设施板块包含所有和 Android 相关的东西(除了数据持久化和 UI)。数据板块应该持有和数据持久化相关的东西,正如我们说过的那样。你不能把这两者弄成 java 板块,由于它们需要访问各种 Andriod 相关的东西。你可以把它们弄成 Android library。

最后,我们将和 UI (包括 Presenter)相关的所有东西分到 UI 板块。你可以明确地将其命名为 UI,但是因为所有 Android 的东西都在这里,我们保留它 “app” 的名字,正如 Android Studio 在创立项目时所命名的那样。

好点了吗?

为了答复这个问题,我丢开 Uncle Bob 的图,将先前形容的组件摊开到图中,就像之前那些我们曾经评估过的架构类型那样。这样做之后,我们得到:

现在让我们来使用与之前的架构相同的评价标准。

它完全分离到板块级别、包级别、类级别,所以应该满足单一职责准则。

我们已经将 Android 和真实世界的东西尽可能地推到边缘,业务逻辑再也没有直接接触 Android。

我们很好地分离类以方便测试。接触 Android 世界的类可以使用 Android 测试例进行测试,没有接触的类可以使用 JUnit 进行测试。可能有人恶意称它为类爆炸,我称之为可测试。:)

这可能很复杂——但值得

看起来很复杂,而且有很多细节,但这是值得的。一旦你把所有都串起来,测试就会变的更容易,BUG 更容易定位,新功能更容易增加,代码更易读和维护,一切都可以完美运行,宇宙都被满足了。

最后

假如你看到了这里,觉得文章写得不错就给个赞呗!欢迎大家评论探讨!假如你觉得那里值得改进的,请给我留言。肯定会认真查询,修正不足,定期免费分享技术干货。谢谢!

image
免责声明:本文为用户发表,不代表网站立场,仅供参考,不构成引导等用途。 系统环境 服务器应用
相关推荐
Android 知识简记:资深架构师带你快速回顾Android各种知识!
Gitter - 高颜值GitHub小程序用户端诞生记
Spring Boot(三):搭建一个简单的RESTful接口项目
第54期 如何优雅解决前台异常 & JavaScript闭包详细图解 & 前台登录,这一篇就够了
iOS播放系统声音,同时震动;播放自己设置声音
首页
搜索
订单
购物车
我的