专栏名称: 非非白
目录
相关文章推荐
成都日报  ·  2025年2月5日|成都海报 ·  2 天前  
成都日报  ·  2025年2月5日|成都海报 ·  2 天前  
成都本地宝  ·  成都租房补贴申请方式及审核流程 ·  5 天前  
重庆市政府网  ·  春节假期前4天 ... ·  4 天前  
重庆市政府网  ·  春节假期前4天 ... ·  4 天前  
51好读  ›  专栏  ›  非非白

[译] Android 架构:Part 2 —— 介绍 Clean Architecture

非非白  · 掘金  ·  · 2019-07-13 02:30

正文

阅读 61

[译] Android 架构:Part 2 —— 介绍 Clean Architecture

在本系列的第一部分,我们介绍了我们在寻找可行架构的道路上所犯过的错误。在这部分,我们将介绍传说中的 Clean Architecture。

当你在谷歌搜索 "clean architecture" 时,你看到的第一张图片是:

它也被称为洋葱架构,因为图看起来象个洋葱(你会意识到你需要写样板代码写到哭);或者是端口和适配器,因为你可以看到右图的一些端口。六角架构是另一个相似的架构。

Clean Architecture 是前面提到的 Uncle Bob 的心血结晶,他是 《代码整洁之道》的作者。这种方法的要点是,业务逻辑(也称为 domain),是宇宙的核心。

掌控你的领域(domain)

当你打开项目时,你应该已经知道这个 app 是做什么的,与技术无关。其它一切都是实现细节。譬如,持久化就是一个细节。定义接口,创建一个快速的粗糙的内存内(in-memory)实现,不要想太多,直到完成业务。然后你可以决定怎样真正地持久化数据。数据库,网络,两者结合,文件系统 —— 或者仍然保留在内存中,或者结果你根本不需要持久化。总之一句话:内层包含业务逻辑,外层包含实现细节。

话说回来, Clean Architectue 有一些特性使这成为可能:

  1. 依赖规则
  2. 抽象
  3. 层与层之间的通信

I.依赖规则

依赖规则可以用下图解释:

外层应该依赖内层。那三个在红色框框内的箭头表示依赖。与其使用“依赖”,也许使用“看见”、“知道”、“了解”这类术语更好。在这些术语中,外层看见,知道,了解内层,但内层看不见,也不知道,更不了解外层。正如我们先前所说,内存包含业务逻辑,外层包含实现细节。遵循依赖规则,业务逻辑既看不到,也不知道,更不了解实现细节。这正是我们努力想要做到的。

如何实现依赖规则取决于你。你可以把它们放到不同的包,但小心“内层的”包不要使用“外层的”包。然而,如果有人不知道依赖规则,没有什么可以阻止他破坏规则。一个更好的方法是把层分离到不同的 Android 模块(modules,即子项目),并在构建文件(build.grale)中调整依赖,这样内层就无法依赖外层。

还有值得一提的是,虽然没人可以阻止你跨层依赖,譬如蓝色的层的组件使用红色的层的组件,但我强烈建议你只访问相邻的层的组件。

II.抽象

抽象原则之前已有所暗示。也就是说,当你朝图中间移动时,东西变得更抽象。 这是有道理的:正如我们所说内层包含业务逻辑,而外层包含实现细节。

甚至可以在多个层之间划分相同的逻辑组件,如图所示。 内层定义更抽象的部分,外层定义更具体的部分。

举个例子说清楚些。我们可以定义一个 “Notifications” 的抽象接口,并将其放到内层,这样你的业务逻辑需要时可以使用它来向用户显示通知。另一方面,我们可以这样来实现该接口,即使用 Android NotificationManager 显示通知来实现,并把该实现放到外层。

以这种方式,业务逻辑可以使用这样的功能 —— 通知(在我们的例子中)—— 但它不了解实现细节:实际的通知是如何实现的。此外,业务逻辑甚至不知道实现细节的存在。来看下面这张图片:

当将抽象规则和依赖规则组合在一起时,结果是使用通知的抽象业务逻辑既不会看到,也不会知道,更不会了解使用 Android NotificationManager 的具体实现。这很好,因为我们可以在业务逻辑毫不知情的情况下切换具体实现。

让我们把这种规则组合和标准的三层架构简单对比下,看看它们各自的抽象和依赖是怎样的以及如何工作的。

在图中,你可以看到,标准三层架构的所有依赖最终都传到数据库。也就是说,抽象和依赖(方向)并不一致。在逻辑上,业务层应该是 app 的中心,但它却不是,因为依赖朝向数据库。

业务层不应该知道数据库,应该反过来。在 Clean Architecture 中,依赖朝向业务层(内层),并且抽象也上升到业务层,因此它们很好地匹配。

这是重要的,因为抽象是理论,依赖是实践。抽象是 app 的逻辑布局,依赖关系是(组件)如何实际组合在一起。在 Clean Architecture 中,这两者是匹配(译者注:指方向一致)的。而在标准三层架构中则不然,如果你不小心,很容易导致各种逻辑上的不一致和混乱。

III.层与层之间的通信

现在我们将 app 分模块,将所有内容分开,将业务逻辑放在我们 app 的中心,并在外层实现细节,一切看起来都很棒。 但是你可能很快遇到一个有趣的问题。

如果你的 UI 是一个实现细节,网络是一个实现细节,业务逻辑在中间,那么我们如何从互联网获取数据,经过业务逻辑,然后发送到界面?

业务逻辑在中间,应该协调网络和界面,但它甚至不知道两者的存在。这是一个关于通信和数据流的问题。

我们希望数据能够从外层流向内层,反之亦然,但依赖规则不允许。 让我们举个最简单的例子。

我们只有两层,绿色和红色的。绿色的是外层,它知道红色的,红色的是内层,它只知道自己。我们希望数据从绿色流向红色,然后折回绿色。该解决方案先前已经暗示过了,看下图:

图的右边部分显示了数据流。数据源于 Controller,经过 UseCase(或者替换成你选择的组件)的输入端口,然后通过 UseCase 本身,最后通过 UseCase 输出端口发送到 Presenter。

图的主要部分(左边)的箭头表示组合和继承 —— 组合用实心箭头表示,继承用空心箭头表示。组合也被称作 has-a 关系,继承被称作 is-a 关系。圆圈中的 “I” 和 “O” 表示输入和输出端口。可以看到,定义在绿色层中的 Controller,拥有一个(has-a)定义在红色层中的输入端口。UseCase(齿轮,业务逻辑,现在不重要)是一个(is-a)(或实现)输入端口,并且拥有一个(has-a)输出端口。最后,定义在绿色层中的 Presenter 实际上是一个(is-a)定义在红色层的输出端口。







请到「今天看啥」查看全文