Android 进阶之设计模式 (转)Android-CleanArchitecture
英文原文:
http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/
http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/
中文翻译:
http://android.jobbole.com/81153/
https://zhuanlan.zhihu.com/p/20001838
对应代码:
https://github.com/android10/Android-CleanArchitecture
https://github.com/zhengxiaopeng/MVVM_Android-CleanArchitecture
https://github.com/zhengxiaopeng/Rocko-Android-Demos/tree/master/architecture/MVVM_Android-CleanArchitecture
对于好的文章真的会有一种忍不住要收藏的感觉,现在就无耻得转载这篇文章分享给大家:
开始
我们知道编写高质量软件是既困难又复杂的:不仅是满足需求方面,还要健壮、可维护、可测试,并且足够灵活以适应增长和变化。这就是“代码整洁之道”的来源,并可以成为开发任何软件应用程序的良好方法。
思想很简单:代码整洁之道代表构建系统的一组实践:
- 独立于框架。
- 可测试性。
- 独立于UI。
- 独立于数据库。
- 独立于任何外部代理。
我们并不要求一定要用四环结构(如图所示),这只是一个示例图解,但是要考虑的是依赖项规则:源码依赖项只能向内指向,内环里的所有项不能了解外环所发生的东西。
以下是更好地理解和熟悉本方法的一些相关词汇:
- Entities:是指一款应用的业务对象
- Use cases:是指结合数据流和实体中的用例,也称为Interactor
- Interface Adapters: 这一组适配器,是负责以最合理的格式转换用例(use cases)和实体(entities)之间的数据,表现层(Presenters )和控制层(Controllers ),就属于这一块的。
- Frameworks and Drivers: 这里是所有具体的实现了:比如:UI,工具类,基础框架,等等。
想要更具体,更生动丰富的解释,可以参考这篇文章或者这个视频。
场景
我会设置一个简单的场景来开始:创建一个简单的小app,app中显示从云端获取的一个朋友或用户列表。当点击其中任何一个时,会打开一个新的窗口,显示该用户的详细信息。
这里我放了一段视频,大家看看这个视频 (需翻墙)大概就可以对我所描述的东西了解个大概了。
Android应用架构
这一对象遵循关注分离原则,也就是通过业务规则让内环操作对外环事物一无所知,这样一来,在测试时它们就不会依赖任何的外部元素了。
要达到这个目的,我的建议就是把一个项目分成三个层次,每个层次拥有自己的目的并且各自独立于堆放运作。
值得一提的是,每一层次使用其自有的数据模型以达到独立性的目的(大家可以看到,在代码中需要一个数据映射器来完成数据转换。如果你不想把你的模型和整个应用交叉使用,这是你要付出的代价)。
以下是图解,大家感受下:
表现层 (Presentation Layer)
表现层在此,表现的是与视图和动画相关的逻辑。这里仅用了一个Model View Presenter(下文简称MVP),但是大家也可以用MVC或MVVM等模式。这里我不再赘述细节,但是需要强调的是,这里的fragment和activity都是View,其内部除了UI逻辑以外没有其他逻辑,这也是所有渲染的东西发生的地方。
本层次的Presenter由多个interactor(用例)组成,Presenter在 android UI 线程以外的新线程里工作,并通过回调将要渲染到View上的数据传递回来。
领域层 (Domain Layer)
这里的业务规则是指所有在本层发生的逻辑。对于Android项目来说,大家还可以看到所有的interactor(用例)实施。这一层是纯粹的java模块,没有任何的Android依赖性。当涉及到业务对象时,所有的外部组件都使用接口。
数据层 (Data Layer)
应用所需的所有数据都来自这一层中的UserRepository实现(接口在领域层)。这一实现采用了Repository Pattern,主要策略是通过一个工厂根据一定的条件选取不同的数据来源。
比如,通过ID获取一个用户时,如果这个用户在缓存中已经存在,则硬盘缓存数据源会被选中,否则会通过向云端发起请求获取数据,然后存储到硬盘缓存。
这一切背后的原理是由于原始数据对于客户端是透明的,客户端并不关心数据是来源于内存、硬盘还是云端,它需要关心的是数据可以正确地获取到。
错误处理
这是一个长期待解决的讨论话题,如果大家能够分享各自的解决方案,那真真是极好的。
我的策略是使用回调,这样的话,如果数据仓库发生了变化,回调有两个方法: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” ,我非常同意这个说法,当然了,有很多不同的方法做不同的事情(不同的实现方法),我很确定,你每天(像我一样)会面临很多挑战,但是遵循这些方法,可以确保你的应用会:
易维护
易测试
高内聚
低耦合
参考资料
Source code: android10/Android-CleanArchitecture · GitHub
The clean architecture by Uncle Bob
Architecture is about Intent, not Frameworks
Model View Presenter
Repository Pattern by Martin Fowler
Android Design Patterns Presentation