入门指南
我们知道,编写高质量的软件很难,也很复杂:急需要满足需求,又要有稳健性,可维护性,可测试性和灵活性,以适应增长和变化。这就是“干净的架构”出现的地方,在开发任何软件应用程序时可能是一个很好地方法。
这个想法很简单:干净的建筑代表了实践,产生的系统是:
- 独立于框架
- 可测试
- 独立的用户界面
- 独立的数据库
- 独立的任何外部结构
不要只用4个圆圈(如图中所示),因为它们只是示意图,但是您应该考虑依赖规则:源代码依赖只能指向内部,并且内圈中得任何东西都不能知道任何关于外圈的事情。
以下是一些与熟悉和理解这种方法的相关名词:
- 实体:这些是应用程序的业务对象
- 使用案例:这些使用案例协调实体之间的数据流,也被称为交互者
- 接口适配器:这组适配器将转换来自用例的实体最方便的格式的数据,也被称为交互者
- 框架和驱动程序:这是所有细节的地方:UI、工具、框架等
有关更好更广泛地解释,请参阅本文
Android框架
我们从一个简单地场景开始:简单地创建一个小城,显示从服务端中检索到的朋友或用户列表,点击其中的任何一个将打开一个新的界面,显示该用户更多的详细信息。
目的是通过保持业务规则对外界不了解任何事情来区分关注点,从而可以在不依赖任何外部元素的情况下进行测试。
为了达到这个目的,我建议是把项目分成三个不同的层次,每个层次都有自己的职责。
值得一提的时,每一层都是哦那个自己的数据模型,这样就可以实现独立性
image注意:我没有使用其他的外部库(除了解析json数据的gson和测试用到的junit、mockito、robolectric、espresso),这样使例子更清楚。无论如何,添加ORMs存储磁盘数据或任何依赖注入框架或任何你熟悉的库,可以使开发更轻松愉快。
表现层 (Presentation Layer)
表现层在此,表现的是与视图和动画相关的逻辑。这里仅用了一个Model View Presenter(下文简称MVP),但是大家也可以用MVC或MVVM等模式。这里我不再赘述细节,但是需要强调的是,这里的fragment和activity都是View,其内部除了UI逻辑以外没有其他逻辑,这也是所有渲染的东西发生的地方。
本层次的Presenter由多个interactor(用例)组成,Presenter在 android UI 线程以外的新线程里工作,并通过回调将要渲染到View上的数据传递回来。
mvp
如果你需要一个使用MVP和MVVM的Effective Android UI典型案例,可以参考我朋友Pedro Gómez的文章。
领域层 (Domain Layer)
这里的业务规则是指所有在本层发生的逻辑。对于Android项目来说,大家还可以看到所有的interactor(用例)实施。这一层是纯粹的java模块,没有任何的Android依赖性。当涉及到业务对象时,所有的外部组件都使用接口。
domain数据层 (Data Layer)
应用所需的所有数据都来自这一层中的UserRepository实现(接口在领域层)。这一实现采用了Repository Pattern,主要策略是通过一个工厂根据一定的条件选取不同的数据来源。
比如,通过ID获取一个用户时,如果这个用户在缓存中已经存在,则硬盘缓存数据源会被选中,否则会通过向云端发起请求获取数据,然后存储到硬盘缓存。
这一切背后的原理是由于原始数据对于客户端是透明的,客户端并不关心数据是来源于内存、硬盘还是云端,它需要关心的是数据可以正确地获取到。
注:在代码方面,出于学习目的,我通过文件系统和Android preference实现了一个简单、原始的硬盘缓存。请记住,如果已经存在了能够完成这些工作的库,就不要重复制造轮子。
错误处理
这是一个长期待解决的讨论话题,如果大家能够分享各自的解决方案,那真真是极好的。
我的策略是使用回调,这样的话,如果数据仓库发生了变化,回调有两个方法:onResponse()和onError(). onError方法将异常信息封装到一个ErrorBundle对象中: 这种方法的难点在于这其中会存在一环扣一环的回调链,错误会沿着这条回调链到达展示层。因此会牺牲一点代码的可读性。另外,如果出现错误,我本来可以通过事件总线系统抛出事件,但是这种实现方式类似于使用C语言的goto语法。在我看来,当你订阅多个事件时,如果不能很好的控制,你可能会被弄得晕头转向。
测试
关于测试方面,我根据不同的层来选择不同的方法:
- 展示层 ( Presentation Layer) : 使用android instrumentation和 espresso进行集成和功能测试
- 领域层 ( Domain Layer) : 使用JUnit和Mockito进行单元测试;
- 数据层 ( Data Layer) : 使用Robolectric ( 因为依赖于Android SDK中的类 )进行集成测试和单元测试。
代码展示
我猜你现在在想,扯了那么久的淡,代码究竟在哪里呢? 好吧,这就是你可以找到上述解决方案的github链接。还要提一点,在文件夹结构方面,不同的层是通过以下不同的模块反应的:
- presentation: 展示层的Android模块
- domain: 一个没有android依赖的java模块
- data: 一个数据获取来源的android模块。
- data-test: 数据层测试,由于使用Robolectric 存在一些限制,所以我得再独立的java模块中使用。
结论
正如 Bob大叔 所说:“Architecture is About Intent, not Frameworks” ,我非常同意这个说法,当然了,有很多不同的方法做不同的事情(不同的实现方法),我很确定,你每天(像我一样)会面临很多挑战,但是遵循这些方法,可以确保你的应用会:
- 易维护 Easy to maintain
- 易测试 Easy to tes.
- 高内聚 Very cohesive.
- 低耦合 Decoupled.
最后,我强烈推荐你去实践一下,并且分享你的经验。也许你会找到更好的解决方案:我们都知道,不断提升自己是一件件非常好的事。我希望这篇文章对你有所帮助,欢迎拍砖。
参考资料
Source code: https://github.com/android10/Android-CleanArchitecture
网友评论