【译】更清晰的Android应用架构-MVP

原文链接Architecting Android…The clean way?

      在过去的几个月中我与公司Tuenti@pedro_g_s@flipper83(两位android技术大牛)两位同事讨论Android应用架构,我决定抽出一点时间写一篇关于Android应用架构的文章。

      通过我过去几个月的学习和实践总结了一些方法,它们将向你展示如何去实现这种清晰的Android架构。

开始前的一些准备

      我们知道开发一个高质量的软件是非常困难和复杂的,它不仅是为了满足功能实现,也应该是健壮、易维护、易测试和适于伸缩构建。这就是“清晰架构”的由来,这可能是开发任意软件程序的最好方法。

      “清晰架构”的概念很简单,产品系统中遵循一系列的习惯原则:

1、与框架分离

2、易测试

3、与UI分离

4、与数据库分离

5、与其它外部组件分离


上图中你所看见的不一定非要4个圆环来表示,因为这只是原理图但你应该考虑到相关规则

源码依赖只能指向内环而内环不能知道它所在外环的任何事情。

下面的一些术语可以让你明白和了解这是种非常好的架构方式:

      实体:他们是程序的业务对象

      用例:他们是链接数据流和实体间的纽带,也可叫作Interactor(翻译成:交互器和耦合器,感觉都不对)

      接口适配器:这些适配器为用例和实体提供相应格式的数据。Presenter(展现层)和Controller(控制层)都奴属于这里。

      框架和驱动:这里是所有的具体实现,如:界面,工具类,框架等等

更加详尽的解释参见这篇文章The Clean Architecture和视频Robert C. Martin - Clean Architecture (貌似需要翻墙)

我们的方案

      我们将从一个简单的案例开始:简单的创建一个app,这个app显示一个从云端取回的朋友或用户数据,当单击列表中的任意一个条目时,然后打开一个新的窗口显示这个用户的具体细节。

      给你们看一个视频,你们就知道我讲的什么:Clean Architecture on Android - Sample App(需要翻墙)

Android架构

      遵循“关注分离”的原则来让内环的业务操作不知道外环的一切,不依赖任何外部元素所以他们易于测试。

      要做到这一点,“我的建议是将这个项目分离成3个不同的层”,这样每个层只关心自己的目标和工作以至独立于其它层。

      值得一提的是,每个层只使用它自己的数据模块来达到独立性(你将在代码中见到那个 data mapper被用来实现数据转换,如果你不在整个应用中跨模块使用只需小的代价)

这儿是一个我建议的三层模式:

注意:我没有使用任何第三方库(除了解析json数据的GSON、用于测试的junit,mockito、robolectri、espresso)这是为了使这个示例更加整洁。在你的开发中,无论如何不要犹豫去添加你所熟悉的存储数据的ORMs框架、依赖注入框架、任何工具、第三方库,这样在编写整个示例过程中可以省掉不少功夫(记住:重复造轮子不是个好习惯)。

展现层(Presentation Layer

       这儿是视图和动画相关逻辑呈现的地方,它就是 Model View Presenter模式的一部分(参见:MVP),但你可以使用其它模式像MVC或MVVM。我不会进一步阐述他们,但这里的Fragments 与Activities仅仅是一个Views,这里不会有逻辑存在于UI逻辑中,并且这里是视图渲染填充的地方。这层中的Presenters结合interactors (用例)在UI线程之外的新线程中执行任务随后使用回调函数传参的形式来渲染视图。

如果你想要一个很酷的MVP和MVVM使用例子,你可以查看我朋友Pedro Gómez的Effective Android UI

领域层(Domain Layer

这里是业务规则:所有逻辑发生在这层. 对于Android 项目你也会在这里见到所有的interactors(用例)实现。

这层是一个纯java模块而不是其它Android依赖。所有外部组件使用接口连接业务对象

数据层(Data Layer

      应用需要的所有数据都来自这层,通过一个UserRepository实现(这个接口在领域层),它使用一个Repository Pattern和一个策略相结合,通过一个工厂,依赖某些条件来获取不同的数据源。例如通过一个id来获取用户,随后获取该用户在磁盘上的缓存数据,如果该用户存在的话,否则将从云端检索数据随后保存在磁盘缓存中。

      这个背后的理念是所有数据对于客户端来说都是透明的,它并不关心来自内存、磁盘、还是云端的数据是怎样传递和获取的。

注意:我的代码实现非常简单,使用文件系统和Android Preferences作为磁盘缓存,这是为了达到学习的目的。重申:如果已有第三方库能胜任这种工作就不要重复的造轮子。

错误处理(Error Handling)

      这总是一个值得讨论的话题,如果你有更好的解决方案请共享在这儿。

      我的策略是使用回调接口, 以数据存储响应为例,回调接口中有onResponse()onError()两个方法。这里封装了一个叫“ErrorBundle”异常处理类:这种方法也带来一些困难,因为这儿是一个回调链直接将错误抛给展现层(Presentation Layer)。代码可读性不太好。

      另外,我的主张是实现一个事件总线系统去抛事件当某些错误发生时,但这类似使用GOTO。当你订阅几个事件时,如果你不善于控制处理它们,你会感到很困惑。

测试(Testing

      对于测试,根据不同层我选择了几个不同的方案:

      展现层(Presentation Layer):使用android instrumentation、espresso集成测试、功能性测试。

      领域层(Domain Layer):使用JUnit+mockito(java模拟测试框架)进行单元测试

      数据层(Data Layer):Robolectric(因为这层有Android依赖)+junit+mockito进行集成和单元测试

我的代码(Show me the code

     我知道你在想我的代码在哪里,没错?这里是github链接代码,在里边你将知道我是怎么做的,关于项目结构不得不提一句,使用模块来区分不同的层:

    展现层(presentation):它是一个Android module,它代表展现层。

    领域层(domain):一个纯java对象模块而不是 Android Dependencies

    数据层(data):一个Android Module,所有数据都从这里检索

    data-test:为数据层提供测试,使用Robolectric时的某些局限性,不得不在一个单独的java模块中使用它

结论(Conclusion)

正如鲍勃叔说,“Architecture is About Intent, not Frameworks”,我完全同意这个说法当然这儿有许多不同的做法(不同的实现)我非常肯定,你和我一样每天面临很多挑战,但使用这些技术,你开发的软件将:

      1、易于维护(Easy to maintain

      2、易于测试(Easy to test

      3、内聚性强(Very cohesive

      4、解耦(Decoupled

我强烈建议你尝试并分享你的结果和体验,也许你能找到其它工作得更好的方法:我们知道持续改进是一件非常好的事。

我希望这篇文章能帮到你,非常欢迎并期待你的反馈!

源码(Source code):

       1、Clean architecture github repository – master branch

       2、Clean architecture github repository – releases

下载源码的朋友注意咯:

        请进入第二个链接去下载:我下载的v0.5.0版,其它版本原作者在里边使用了Dagger 2注解框架,如果你没有使用过dagger 2, 当你在看源码的时候可能很茫然(比如说我)

        我用UML的形式分析了下作者的这个源码(v0.5.0版)并把图上传在这里:希望能帮助你快速阅读 

下载地址

延伸阅读(Further reading)

       1、Architecting Android..the evolution

       2、Tasting Dagger 2 on Android

       3、The Mayans Lost Guide to RxJava on Android

       4、It is about philosophy: Culture of a good programmer

链接和资源(Links and Resources

      1、The clean architecture by Uncle Bob

      2、Architecture is about Intent, not Frameworks

      3、Model View Presenter

      4、Repository Pattern by Martin Fowler

      5、Android Design Patterns Presentation

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容