什么是架构
软件架构绝对不只是框架的堆砌,在我看来,架构是为了方便软件维护、扩展、安全性、切入性(我也不知道有没有人提出过这个关键字,因为的确很少看见,简单来说:我这里说的切入性就是指一个以前没有接触过这个项目的人,能快速加入到这个项目中,对项目进行维护、修改和扩展)
维护性
一个好的软件(不一定是成功的软件,这里说的好只是程序员认为的代码方面)肯定是能方便维护的,出了问题能快速定位,需要修改时能快速修改,并且在一定程度上不会说一修改就一堆bug,这就是我认为的可维护性,当然后面要说到的切入性其实也算是维护性,不过为什么单独放出来在切入性时我会详细说明。至于怎么样才能使一个软件维护方便,我觉得有以下几点:
1.代码规范
一份代码如果没有遵循任何规范,那么我相信它的可维护性是很差的,就算是你一个人做出来的,估计过了几个月去修改的时候也会冒出一句这TM是什么鬼
2.框架稳定性
很多时候很多开源框架刚出来的时候,也许功能十分强大,但是毕竟刚出来,没有经过充分的测试,所以还是会或多或少存在一个不稳定因子,所以建议在选择框架时尽量选择成熟稳定的框架,哪怕功能和性能的确比不上刚出来的框架。当然这也不是说完全不用刚出来的框架,毕竟都不用,那么它也永远成熟不了,至于到底用不用和怎么用,本文的框架选择和使用篇也会详细分析说明
3.封装
本来想说AOP的,但是个人觉得很多专业性的名词也比不上一些通俗的形容,毕竟本文主要的目的是让人理解,不是提出理论。封装这个在Android中是经常使用的,简单来说就是把一些常用的、通用的东西进行一个封装,通过统一入口进行调用,这样出问题就只需要修改一个地方,就能全部修改过来。同时封装也要注意一些常见的坑,比如我曾经就踩过的context坑,当时封装了一个UIUtils(主要是针对UI相关的工具类),因为很多方法都要使用context,所以直接application传进去,保存了,所有方法都不用传context,但是这样却出了一个bug,那就是有些操作用application的context是有问题的。当然这种还比较好处理,但是如果因为封装导致内存泄露,这就难以查找了,比如你传入了一个activity的context,但是activity已经关闭了,但是因为你封装的方法里面还在继续使用这个context,所以activity的内存也是不会释放的,所以封装的时候也一定要注意,不要给自己挖坑
4.耦合
针对耦合这个东西相信很多文章都说过了,如何解耦合,不过个人感觉解耦合这个东西也要适度,不要因为解决一点耦合,花了大量的代码,浪费了大量的性能,所以解耦合这个东西就一定要把握这个度,过度设计是有问题的设计,这点我是赞同的。同时很多时候封装会导致一些耦合的问题,比如我曾经一个项目中就有个这个一个情况:
因为项目中使用了滑动选取身高的WheelView,因为当时是弹Dialog出来选择的,所以当时想也没想就直接封装了一个Dialog,后面又出来了一个选取年龄的,然后又封装了一个Dialog,以至于到后面封装的Dialog有7,8个了,但是有些界面因为选中后的处理有些不一样,导致Dialog里面的代码混乱,所以后面就直接简单的封装了一个Dialog,传入需要选中的数据集合和选中监听,这样就用了一个Dialog就能处理多种数据的选择,还能根据不同界面分别处理,当然这样也不是万金油,毕竟这样每个界面都需要自己实现监听,所以很多时候有利就有弊,至于具体怎么取舍,就看利是否大于弊
扩展性
扩展性简单来说就是当程序需要新的功能时,能否对其进行扩展以及扩展的难度来判断,如何提高扩展性,我觉得有以下几点:
1.抽象接口
这点相信大家都经常遇到,比如Android的点击事件,你想要实现什么样的点击效果,自己实现一个点击监听,然后设置给控件就可以了
2.元素重用
很多时候,很多功能模块可能使用到相同或者类似的元素,如Android中的一些布局,这些如果抽取出公共部分在进行扩展的时候方便对其快速扩展,当然这个是项目一开始并不能预见的,所以需要在开发中不断的去重构
3.单一职责
这个其实就是面向对象的单一职责,比如前面提到的Dialog,一开始只有选择身高的功能,看起来是单一职责,但是其实相关处理也包含在其中,所以那个Dialog类本身职责已经不单纯,当然如果一直只有这一个,这样做没有任何问题,也能看做单一职责,但是如果有多个选择和处理的时候,就必须对其重构了
4.替换性
替换性包含里氏代换但是也不仅仅是里氏代换,比如常见的Android布局不同,但是其显示内容大致相同,这样写布局的时候就可以对相同内容的控件指定相同的id,这样就算替换布局,也不用重写ViewHolder,当然对于Adapter也仅仅只需要替换相应的布局就ok
5.耦合
这个和维护的耦合相同,毕竟很多地方本来就存在交叉,所以就没有必要再说了
安全性
个人觉得数据安全性并不仅仅是数据安全,还有程序的一些操作安全性(简单来说就是避免程序出现一些非崩溃性异常)
1.数据安全性
数据安全就包括数据抓取、数据拦截以及数据修改。当然这些并不能完全避免,只能是由我们写出尽量安全的代码,比如关键数据使用HTTPS以及对数据进行md5验证完整性,对于数据修改,可以通过多文件多地址保存文件修改记录,来确定保存的数据是否被修改,毕竟Android只要获取root权限,就能对很多文件进行修改了
2.操作安全性
简单来说经常遇到的一个问题,比如按钮的点击事件,有可能这个点击事件是请求网络或者打开Activity,这样就会存在事件还未处理完成再次收到事件,只要你一直猛点,肯定可以的,所以这样就需要我们对控件事件进行一些封装,比如打开界面的,可以在点击后禁用按钮,界面打开完成后才启用,请求网络的可以在开始就禁用按钮,请求结果反馈了才启用(不管是请求成功或者失败)
切入性
切入性就是当另外一个从未接触过此项目的人,能快速进入这个项目进行开发,当然想要切入性好,前面的维护和扩展是必须要满足的,下面我就说说我认为能增加切入性的一些点
1.文档
开发都不喜欢写文档,这是肯定的,但是每当我们去接手一个项目的时候,发现没有文档估计就要开始骂娘了,所以文档不仅要写,还要写的规范。我认为开发中必须要有的几个文档:代码规范文档(比如包名规范,文件命名规范,id命名规范等等,具体依据项目情况而定)、接口文档
2.注释
每个类必须要有注释,方法也要有注释,同时也要标注好方法最后修改人是谁,这样出现疑问或者问题别人就知道该去找谁了,当然有些方法是不需要有注释的,比如重写父类的方法,只是如果方法内逻辑很复杂,可以在方法中添加一些对逻辑的说明。当然注释也不是越多越好,具体注释该怎么写,Google最清楚,不能Google百度也行
3.包名
什么类放什么包简单,但是一但一个包中的类太多,也是非常不方便,所以正确的分包也是非常重要的,目前常见的Android分包包括针对功能分包(不是指程序功能,而是指UI,http,bean这些功能),还有就是模块分包(这就是程序的功能了,比如login,user等),当然具体怎么分包需要团队协商,防止一个包中类太多,而我现在一个程序很大的情况下,采用的分包是先功能分包,再模块分包,比如:
wang.raye.demo
|-activity
| |-user
| |-login
|-fragment
| |–user
| |–login
这样能减少每个包的类数量,当然如果项目本身并不是很大,可以完全不用这种分包模式,毕竟如果只是小项目,这样会使项目变得更加难以阅读
MVC 还是 MVP
现在针对移动端开发,衍生了很多种架构,如MVC、MVP、MVVP,当然这里着重分析MVC和MVP,毕竟MVVP我也只是了解过一下,没有详细接触,至于什么是MVC和MVP我也不想做过多描述,这类的文章实在太多,这里主要分析一下什么情况下用MVC和MVP
MVC
MVC是以XML布局为V(视图),Activity或Fragment为C(控制器),数据实体为M(模型),但是因为XML的局限性,所以其实我们还是需要在Activity或Fragment中对视图进行操作,所以这也就是为什么那么多人抵制MVC的原因,因为这也算不上完整的MVC
优点:开发迅速,结构易理解
缺点:当一个界面业务逻辑一多,不方便维护
MVP
MVP是为XML配合Activity或Fragment为V(视图),同时抽象出接口,界面相关业务抽离出来的P(Presenter)同时通过视图接口来更新UI,数据实体为M(模型)
优点:业务发生变化时易修改,同时能减少修改过程中引发bug,也能将多人协同开发充分调用起来(并不是针对一个人负责一个模块的模式,而是多人协同开发一个模块)
缺点:开发速度会有所降低
所以对比2种架构,发现MVC适合不需要太多业务逻辑和功能性少的APP,比如数据展示类应用,MVP适合每个界面有复杂逻辑以及大型多人开发的APP
框架选择及使用 如何选择框架
1.稳定性
如果框架本身就不稳定,那么导致的结果就是程序本身也会漏洞百出,所以选择框架一定要选择经历过考验的稳定的框架
2.扩展性
随着程序功能的增加,以前的框架可能会出现功能不足的情况,但是因为这点是不可预见的,所以我们选择框架时一定要了解好框架本身的扩展性如何,或者对框架有较深的理解,能够自己扩展框架,当然有些框架解决的问题比较单一,一般也不用担心过多的扩展性,比如Butterknife或PreIOC这类单一性框架,但是有些框架经常需要配合做一些操作,比如图片加载框架,常见的一些就是清理图片缓存、获取图片缓存大小、显示圆角或者圆形图片, 常用的图片加载框架UniversalImageLoader都提供了相关的方法或接口来实现
3.封装性
封装性是指能否针对框架进行二次封装,以及封装后的耦合度,详细会在使用篇说明
如何使用
选择好了框架千万不要拿来就用,因为再好的框架也有它局限的地方,当然你也可以简单的在遇到这个框架不能实现的时候,添加另外一种框架,只是这样项目会越来越大,对于APP来说APK也越来越大,65535 的问题也会提前出现,所以为了方便以后有可能出现的切换框架,以及防止初期对框架使用不熟悉而引发出新的bug,在选择好了框架后,一定要对框架进行二次封装,当然有些框架是不需要二次封装的,比如前面说的单一性的框架Butterknife或PreIOC,但是像UniversalImageLoader、OKHttp等框架,必须要进行二次封装,至于封装原则,则是封装后,调用框架对于调用代码来说是透明的,简单来说,就是对于框架调用都通过一个统一的入口进入,并且调用时,不需要传入任何跟框架相关的东西,如果必须要传入接口,可以通过继承框架来实现新的接口传入,这样在真正的使用框架的地方,没有任何关于框架的引用
封装的好处
之所以要这样封装,最大的好处就是一旦框架不能满足需求时,需要进行框架更换时,只需要换掉框架,同时修改统一入口处的代码,就能快速的替换整个框架
以上就是我在Android APP架构上面的一些心得,此文可能并没有教你快速搭建一个框架,只是指明搭建框架时需要注意和搭建框架的一个方向,当然软件是死的人是活的,具体项目具体处理。