type
status
date
slug
summary
tags
category
icon
password
<ins/>
相比其他框架,在 Flutter 中保持代码的清晰与可维护性显得尤为重要。因为 Flutter 中逻辑代码和 UI 代码都使用 Dart 编写,这导致业务逻辑容易和 UI 代码混在一起。一方面,这种方式可以快速构建应用程序,因为 Flutter 加速了开发流程;但另一方面,当业务逻辑和 UI 代码到处混杂时,大型项目容易崩溃。即便是像 BLoC(或其子集 Cubit)这样的优秀状态管理方案,在代码可扩展性方面也可能显得力不从心。
秘诀
保持应用程序可维护的秘诀是什么?答案是由“Bob 大叔”提出的 Clean Architecture(清洁架构)。我们应该将代码分成独立的层,每层依赖于抽象,而不是具体实现。
如何在代码中实现这样的独立性?下面这张分层示意图中,水平箭头(→)表示依赖流向。例如,实体(Entities)完全独立,仅用例(Use Cases)依赖于实体,依次类推。
这听起来可能有点抽象,比如什么是控制器(Controllers)和网关(Gateways)等。接下来,我们将深入探索 Flutter 中的清洁架构,所有疑问都会变得清晰。
项目目标
我们将在这个项目中使用 Firebase 作为后端服务,并选择 Cubit(BLoC 的子集)作为状态管理工具。当然,你也可以选择其他状态管理方案。值得注意的是,清洁架构的核心并不依赖于特定的状态管理工具。
Flutter Clean Architecture
接下来的问题是:如何在 Flutter 中具体实现 Clean Architecture?这里介绍 Reso Coder 提出的清洁架构模型。如果你看过他的 TDD(测试驱动开发)系列,可能对这张图已经很熟悉。如果没看过也不用担心,我们会清晰地讲解这个架构,尽管我们会稍作调整来适应实际需求。
项目结构
我们的项目结构如下图所示。正如前面提到的,我们会稍微调整 Reso Coder 提出的架构模型。我们将使用一个
features
目录,里面包含三个层次:Domain、Data 和 Presentation。小提示:
在 Android Studio 中,你可以安装 Clean Architecture 插件来快速创建项目结构。
在 VS Code 中,可以安装 这个插件。
Presentation(展示层)
展示层负责在屏幕上显示内容,并通过控件触发事件。这些事件监听状态变化或其他等效机制(如果你不使用 BLoC / Cubit)。
注意:在展示层中,逻辑处理器(如 BLoC / Cubit)不会包含太多逻辑,而是将所有任务委派给用例层。
展示层结构
展示层的结构如下:
- Cubit 类存放在
Cubit
目录下。
- 页面存放在
Pages
目录下。
- 其他小组件存放在
Widgets
目录下。
每个目录下会进一步划分子目录或文件,如下图所示:
Domain(领域层)
领域层是最核心的部分,包含核心业务逻辑(用例)和业务对象(实体)。该层完全独立于其他层,但其他层(如展示层和数据层)会依赖于它,因为它包含所有抽象类和契约类。
注意:
用例类负责封装应用程序特定业务场景的逻辑。
在领域层的存储库(Repository)处于数据层与领域层之间。尽管它从数据层获取数据,但领域层仍然独立。原因在于领域层的存储库仅定义契约,而具体实现则在数据层中完成。
提示:
依赖反转原则(SOLID 原则之一)规定,层与层之间的边界应通过接口(Dart 中的抽象类)处理。
领域层结构
领域层的目录结构如下:
Data(数据层)
数据层包含存储库的实现(即领域层中定义的契约的具体实现)和数据源(远程数据源与本地数据源)。
- 远程数据源:如 Firebase(Cloud Firestore)、API 或其他数据库。
- 本地数据源:用于缓存数据。
存储库决定是返回真实数据还是缓存数据。
模型类负责数据转换,例如:
- JSON ↔ Dart 对象
- 快照 ↔ Dart 对象
模型类会继承实体类,并添加一些附加功能(如数据转换)。
数据层结构
数据层的目录结构如下:
总结
到此为止,我们已经构建了 Instagram 克隆项目的基础目录结构。接下来,我们会先从 UI 开始,然后再逐步实现其他功能。
<ins/>