到目前为止,在这个系列中,咱们已经介绍了一些初学者的错误,并经过了Clean架构。 在最后一部分中,咱们将介绍拼图的最后一部分:标签,或者更确切地说:组件。android
首先,我将删除咱们在Android项目中不使用的东西,而后添加一些咱们使用的东西,但在原始的Bob叔叔图中找不到。 它看起来像这样:数据库
我会从最抽象的中心走到边缘。缓存
Entities,即Domain 对象或业务对象,它们是App的核心。 它们表明了APP的主要功能,您应该可以经过查看它们来了解APP的功能。 它们包含业务逻辑,但仅限于它们 - 验证和相似的东西。 他们不会与外在世界的细节进行互动,也不会处理持久性。 例如,若是您有新闻APP,则这些实体将是类别,文章和商业广告。架构
Use case,又名interactors,又名business services,是Entities的扩展,是业务逻辑的延伸,也就是说。 它们包含的逻辑不只限于一个实体,而是处理更多的实体。 一个好Use case的一个指标是,你能够用一种通用的语言描述它在一个简单的句子中的做用 - 例如,“从一个帐户转移到另外一个帐户”。你甚至能够用这样的命名来命名该类,例如,TransferMoneyUseCase。app
Repositories用于坚持Entities。 就这么简单。 它们被定义为接口并用做想要对Entities执行CRUD操做的用例的输出端口。 另外,它们能够公开一些与持久性相关的更复杂的操做,例如过滤,聚合等。 具体的持久性策略,例如数据库或因特网,是在外层实现的。 例如,您能够命名接口AccountRepository。dom
若是你熟悉MVP模式,Presenters就会作你指望他们作的事情。 他们处理用户交互,调用适当的业务逻辑,并将数据发送到UI进行渲染。 这里一般有不一样类型的模型之间的映射。 有些人会在这里使用Controller,这很好。 咱们使用的Presenter正式被称为监督Controller。 咱们一般在每一个屏幕上定义一个或两个Presenters,具体取决于屏幕,Presenter的生命周期与视图的生命周期相关联。 一条建议:尝试在Presenter上将您的方法命名为技术不可知论者(technology agnostic)。 假设您不知道视图实现的技术。所以,若是您在视图中具备名为onSubmitOrderButtonClicked和onUserListItemSelected的方法,则处理这些事件的相应演示者方法能够命名为submitOrder和selectUser。测试
这个组件在通知(再次)例子中已经被取笑了。它包含了诸如传感器,警报,通知,播放器,各类Managers等Android提供东西的实现。它是一个由两部分组成的部分。第一部分是业务逻辑用做与外部世界进行通讯的输出端口的内部圈定义的接口。第二部分,这是在图中绘制的,是这些接口的实现。所以,您能够定义例如名为Gyroscope,Alarm,Notifications和Player的接口。注意名称是抽象的和技术不可知(technology agnostic)的。业务逻辑不关心如何显示通知,玩家如何播放声音,或陀螺仪数据来自何处。您能够制做将通知写入终端,将声音数据写入日志或从预约义文件收集陀螺仪数据的实现。这样的实现对于调试或建立肯定性环境是很是有用的。可是,您固然必须制做诸如AndroidAlarm,NativePlayer等实现。在大多数状况下,这些实现只是围绕Android的Manager 类的包装。fetch
将存储库的实现放在这个组件中。全部这些底层持久化的东西应该在这里:DAO,ORM的东西,Retrofit(或别的东西)的东西,JSON解析等。你也能够在这里实现缓存策略,或者只是使用内存中的持久性,直到完成与应用程序的其他部分。咱们最近在团队中进行了一次有趣的讨论。问题是这样的:资源库是否应公开诸如fetchUsersOffline(fetchUsersFromCache)和fetchUsersOnline(fetchUsersFromInternet)之类的方法?换句话说,业务逻辑应该知道数据来自哪里?看过这篇文章的全部内容后,答案很简单:不。可是有一个问题!若是关于数据源的决定是业务逻辑的一部分 - 例如,若是用户能够选择它,或者若是您有明确的离线模式的应用程序,那么您能够添加这样的区别。可是我不会为每一个抓取定义两种方法。我可能会在资源库中公开方法,如enterOfflineMode和exitOfflineMode。或者,若是它适用于全部存储库,则可使用输入和退出方法定义OfflineMode接口,并在业务逻辑端使用它,让存储库查询该模式并在内部进行肯定。spa
将与Android用户界面相关的全部内容放在这里 Activities,fragments,views,adapters等完成。设计
下图显示了咱们如何将全部这些组件分红Android Studio模块。 你可能会发现另外一个更合适的部门。
咱们将entities,use cases,repositories和device接口分组到domain模块中。 若是您想得到额外的挑战,并得到永恒的荣耀和彻底干净的设计,您可使该模块成为纯Java模块。 它会阻止你在这里使用快捷方式并将相关的东西放在Android上。
device模块应该包含与Android相关的全部内容,而不是数据持久性和UI。 正如咱们已经说过的,data模块应该包含与数据持久性相关的全部内容。 你不能让这两我的进入Java模块,由于他们须要访问各类Android的东西。 你能够将它们变成Android库。
最后,咱们将与UI相关的全部内容(包括presenters)分组到UI模块中。 您能够明确地命名为UI,但因为Android中的全部东西,咱们将其命名为“app”,就像Android Studio在建立项目时命名的那样。
为了回答这个问题,我会抛弃Bob叔叔的图表,并将以前描述的组件扩展到像咱们用来评估之前类型的体系结构的图表。 这样作后,咱们获得这个:
如今让咱们应用咱们在之前的体系结构中使用的相同条件。
它彻底由模块级别,封装级别和类级别分开。 因此SRP应该获得知足。
咱们尽量将Android和现实世界推到了最外面。 业务逻辑再也不直接接触Android。
咱们有很好的分离类,很容易测试。 接触世界的类可使用Android测试用例进行测试; 可使用JUnit测试它。 有人恶意可能会称这类爆炸。 我称之为可测试的。:)
我但愿我精心挑选的标准并非为了支持Clean架构而编造的,它会让你尝试一下。 这看起来很复杂,并且这里有不少细节,但它是值得的。 一旦你将全部东西链接起来,测试就容易得多,错误更容易隔离,新特性易于添加,代码更具可读性和可维护性,一切正常,世界满意。
因此,就是这样。 若是您尚未这样作,请查看之前的系列文章:Mistakes 和 Clean Architecture.。 若是您有任何意见或问题,请在下面留言。 咱们老是有兴趣听到你的想法。
阅读咱们的Android Architecture系列的第四部分。
您还能够查看其余部分:
Part 5: How to Test Clean Architecture