APP架子迁移指南(二)

接上一篇,这一篇开始用android来解释MVP概念、八股式的架子结构和命名规范。我在准备这篇文章的时候还看到不少在MVP基础上衍生的架子思路,底子是MVP没错,但命名有区别、复杂度变了、架子也用到了module拆分而不单纯用包进行拆分,所以接下来会基于googlesamples推荐的命名、架子结构来重构我的约炮APP,我会pull个新的branch来对应各个章节,接下来会从我认为合适的难度适中、实用的方向继续重构。

搭架子切忌过度!。搭架子就是为了隔离代码,该干嘛干嘛(说白了就是就是读数据的放一个文件夹,界面放一个文件夹)。现在流行的“domain”、“repository”、“Service”等命名,你若.net出家肯定会心一笑:“又TM来装逼了”--这些命名和早年的"module"、“Biz”、“helper”命名如出一辙(你可以去苏菲论坛看看大神几年前写的,至今也非常好用的那套.NET通用库)。

当你觉得看不明白的时候就该收手了。还是再强调一次这种观点,比如我为了准备文章看了这篇clean-architecture和这篇mosby。一脸的懵逼,并不是因为鸟语差,我可以毫不费劲的看完,而是因为我在看的时候脑海里一直深深的感觉“我有必要这样搞么?”,后面我突然明白,广点通2毛钱收入不是因为架子差,而是因为crash太多,哈哈哈。看不明白了还要继续装逼,就像刚开炉罩就寻思怎么承载千万级并发一样傻逼。你只要记住哪个文件夹放什么代码文件,干些啥这种层度,就足够受用了。

顺便预告一下,在后面讲IOS端,我会来讲讲MVC模式怎么结合搭架子的思路,正好也为出文章给点动力马拉松一次把一直想做的ios端做了(因为到现在用广点通收入才2毛钱不想弄,哈哈哈)。IOS现在炒MVVM火的很,关于MVVM模式我是很拒绝的,在早年做silverlight的时候用过,觉得太麻烦了(这也是我想提的观点,不要过度),数据绑定这件事,逻辑简单还好,要做很多其他工作,你光是写架子的基础代码就够折腾了。在后面讲PHP端(Laravel)的时候,我就聊聊文件夹该怎么放,文件名该怎么取显得很专业,以伟大又优雅的laravel框架来总结下全篇。

值得看的文章和项目

自从微博关注了很多大神和几个开发博客之后,各种技术文章炸屏有没有..尤其是像我这种好奇心胜的,关注的方向不少,看得很压抑啊。这里我先给几个我认为你应该学习的文章和项目,比我写的好、写的生动(打星标是指我认为的阅读友好度)。

项目,要发现新的会编辑添加:

白话MVP模式

在学习架子的过程中,各种翻译差异和理解误差造成了诸多困扰,什么是“业务逻辑”?“领域层”?“数据映射层”?很烦有没有,尤其是早期网上的文章作者都是大神,觉得人民群众都是学霸,也不给点例子就用无比晦涩的描述带过去了。好在现在很多大神发现了这一点,开始用生动轻巧的语言来描述这些概念。前面推荐的文章相信你也吸收的差不多了,我就结合实际细节再白话一遍MVP模式(我没老司机带,理解偏差请纠错)。

你跟我一样吗

以前你是不是在一个叫data或者bean或者model的包里面放了很多数据实体?如“UserBean”,定义用户的名字、性别字段,添加get/set方法,持久化(Parcelable),用gson或orm的话还会加上@SerializedName、@Table之类的注入。


你定义数据的地方

接下来你会把网络访问哪个网址取得用户数据的代码写在一个叫Manager或者Api或者Network的包里面,json数据从网络读取并通过gson转换你会写在listview所在的fragment或者activity文件中(比如叫loadNewData)。


你定义网络访问的地方

你把网络访问数据转换为listview可读取数据的地方

如果你是有心之人,那么你还会写sqlite操作的逻辑并放在一个叫dao或者db的包,把数据写入到sqlite中去存起来。每次从数据库读或者写,会用asyncTask执行(可能你会新建一个叫task的包或者listview所在的fragment或者activity文件中,当时我图省事没弄)

如果你拿着票子想让功能更健壮,上传数据的代码写在service里面,那就会添加一个service包;需要添加notification,那么你会新建一个叫boardcase的包和notification的包,一个存广播、一个存notificaition;图片处理、时间字符串处理等功能写在一个叫utils的包里面;第三方轮子View控件写在一个叫widget的包里面。

发现啥问题没

写代码讲究个“逻辑分离”,简单讲读写数据的凑一堆;读写网络的凑一堆;界面交互(包括数据显示)凑一堆,这样的好处就是维护起来方便,易于扩展(项目小没这种体验?那在硬盘里面,代码放一个文件夹,ui设计素材放一个文件夹,文档放一个文件夹,要用的时候直接打开哪个文件夹心里了如指掌,这样理解了吧)。

那其他文章里总是提“业务逻辑”、“功能逻辑”,怎么区分呢。以登录功能来举例:

  1. 你用手机号或者微信登录,验证格式或者通过微信获取token和userinfo,都是为了实现登录功能,但是方法不同,完成这些操作你可以调用umeng或者第三方的原生SDK,为这些写代码,算业务逻辑。
  2. 把用户注册信息发送到服务器保存,并接收服务器反馈(比如保持用户登录状态的token),存进preference或者数据库,这些代码算业务逻辑。
  3. 怎么把数据就存入preference或者数据库,无非就是增删改查,那么这部分代码就算功能逻辑。

为实现具体业务功能写的代码,叫做“业务逻辑”。登录的时候获取人员位置、短信验证码都算。并且,你存入数据库之前需要修改服务器反馈的数据(比如从微信拿用户性别是m、f,你想存为0、1表示),这种代码也算入业务逻辑中去的。在各种架子中,“domain”、“service”、“repository”里面干的全是这些事。

为实现可复用的功能函数代码,叫做“功能逻辑”。不管业务是登录还是注销,想访问网络或者读取数据库,最后都会调用平台的方法吧,那么如何调用平台方法的代码,就是功能逻辑。如访问网络,不管是想post登录的网址或者post注销的网址,都会用到调用android网络访问的代码;如增删改查,不管你是preference或者sqlite,最后都会调用android方法执行;如数组转换、临时文件管理、图片压缩、检查字符串是否为空、md5加密、时间格式化等等,你可以写一个叫utils的包保存这些代码。boardcase、receiver、sevice、notifacation都应该以包为单位分离。


很多轮子已经帮你搞定了,比如读取图片的Glide或则网络访问的okhttp

按照这样的思路进行细分,那么很明显的,上一节中目前我app的框架虽然包的结果清晰,但是读起来是一团糟的,而且确实是“读”起来一团糟,比如index的fragment代码行数过高,我不得不简单的把代码“挪”到一个BaseIndexFragment中,简单的把数据读取和界面交互的代码分开便于阅读。MVP模式就很好的解决了我这样不专业的分离方案,提供了标准模板,什么代码该放哪就放哪。

Model层不再只是放数据实体了

按照MVP的说法,Model层是要把所有和数据有关的代码都放在这里的,也就是说定义数据实体、网络读取json并转换为数据数组、数据库读写数据这些代码全部都写在这里。Presenter层想获取数据,那就一定是已经处理好不需要再处理的数据了,怎么获取数据(从网络或者数据库)的、数据怎么转换的,通通都不用管。


数据处理全部移步Model层

在项目里面体现在什么地方呢,listview所在的fragment中,从网络获取json并转换为数据数组操作的代码从原来的loadNewData函数中移到了Model层的XXRemoteDataSource文件中去。从数据库获取数据的asyncTask代码移到了XXXLocalDatasource中去,判断显示本地数据还是网络数据的代码移到了XXXDataRepository中去。这样一来,fragment中的代码一下就清爽了很多,是不是感觉很好?我们继续看Presenter。

Presenter层就像电梯

数据读取前、读取后该干嘛?点击登录按钮后服务器返回登录状态该跳转首页还是提示密码错误?这些事都是Presenter干的:把Model层准备好的数据显示在界面上,有交互时提醒Model层数据需要更新。就像一台电梯,人进来,去按摩到3楼,去ktv到4楼,去开房到5楼。进来的是男是女你不需要关心,出去干嘛你也不需要关心。

在Presenter层里面会大量用到接口(interface)定义行为。以前是不用接口的,第一嫌麻烦,第二人家是团队这样方便测试,我只搞暴力测试没那么讲究。现在还是老老实实用吧,如果不习惯你可以先实现功能,再重构接口,或者把常用的增删改查先复制进去用,慢慢习惯。


要开始写接口了,有点心理准备

这时你可能会有一定的困惑,如果有涉及诸如IM通信、百度LBS定位这样功能逻辑的代码,该放哪里?这些代码可能需要依赖上下文(就是构造函数要context或者activity,你肯定见过),我认为应该放到utils包里面。传递参数、状态成功或失败该怎么处理的代码才放到Presenter中去。也就是说和界面有关的代码才放在Presenter中,其他按业务逻辑或功能逻辑能归类的尽量重构。

View层不是指的控件

这里的View是思想,不是具体指按钮控件或者列表。你可以脑补一个画面:View和Presenter在床(fragment或activity)上搞基,一会Viwe在上面,一会Presenter在上面。


View是让你在通知界面哪个该隐藏了,哪个该刷新了

从一个简单的新闻列表,到复杂点有轮播banner、多级下拉菜单,都可以在View层中表达控件的状态和交互动作,通知presenter这个控件被点击要执行啥操作了,或者prenseter通知这个控件应该被隐藏或显示了。View层在MVP结构中,反而是写代码量较轻的一块,只要Model和Presenter写好了,明天在github上看到一个更酷炫的轮子或者想添加一个动画效果,都是很清爽的事。

聊聊别的

我相信你已经看过我推荐的那几篇文章了,那么我这很白话的把mvp模式讲了一遍,用很多平时会用上的细节来解释,应该对概念的把握比较深刻了。那么在我把我的项目更新完成写下一篇之前,我们再聊聊别的。

RxJava和handler到底该用哪一个?

你们是不是也喜欢把自定义的handler写在fragment文件里面?或者asyncTask之类的,或者adapter。功能简单点的倒是问题不大,这样写又快又省事,阅读性也可以。但要是你遇上登录功能,首先通过umeng获取用户信息,成功了再从服务器获取七牛的上传token,然后传图片,图片上传成功把头像地址和用户信息post到数据库里面,成功了从服务器获取token存app数据库。像这样“圆环套圆环”的业务代码,画时序图都挺麻烦,用handler可以写,但是隔一段时间再阅读起来就很不开心了。


频繁的网络访问会让handler变得臃肿

RxJava的流编程思路就可以很好的解决这种问题,虽然代码量并不见得变少,但却是可以让阅读起来开心一点。至于网络模块你用async-http-client或者okhttp或者volley,或者最近很火的retrofit2,这就看自己了吧。我一直在用async-http-client,够老的轮子了吧,也没觉得有什么不妥的(可能我的网络访问规则太简单了)。另外你想当技术网红的话,RxJava+Retrofit2是必备神器,但要.net出家的人,还是呵呵吧,没必要炒到这种高度。

我的文章肯定有问题,求指正

我没老司机带,全靠github和逆向工程学习。肯定会有问题,还请发现了指正,非常感谢!qq群533838427

posted @ 2016-06-03 16:54  保安保安  阅读(1010)  评论(0编辑  收藏  举报