背景:地图对于很多应用来说已经不是一个稀罕的东西了,很多软件里都有这个功能,特别是那些房产类、外卖类的应用,地图可以算是其中一个很重要的模块。通常情况下,地图是在这些应用的早期开发的,赶着时间开发,因此难免会存在设计粗糙、耦合严重的情况。随着产品线的不断发展,地图模块也变得越来越臃肿,来一个功能就往里面扔,久而久之,哪怕只是对它做一点小的改动,都会比较费劲。这也是为什么很多开发者不愿意产品老是调整地图的原因,因为实在是改不动了。
然而逃避终究不是解决问题的办法,放着不管,只会越来越严重,有病还是要趁早治。
今天我们就来看一下,怎么给一个臃肿的、已经病入膏肓的地图模块做手术。
医生看病时要先了解病人的情况,然后对症下药,那些胡乱开药的医生一定都不是好厨子。因此首先应该花点时间好好的了解这个“病人”的情况。在看了一天的自家项目中地图模块的“病”后,我总结了一下我们项目中地图模块的问题,这个问题,应该也是大部分地图模块的问题,在每一个问题的后面给开出对应的“药方”:
声明了大量的用来记录搜索条件的全局变量
。在地图中通常会有搜索的功能,需要记录用户的各种搜索条件,像我们这种房产类的软件,查询条件要有十五六个,然后就很黄很暴力的声明了十五六个全局变量来进行记录,再加上存放查询结果的数组、字典和其他的中间变量,那简直叫一个酸爽啊。其实我们完全可以声明一个模型来管理这些搜索条件、存储搜索结果以及其他的中间变量,这样做的好处很多,不仅可以很方便的设置默认值。并且在其他类中要使用数据的话,只需要耦合一个模型就好了,方便管理,另外还可以通过优秀的框架(自行百度)将模型直接转换为对应的字典,传给服务器,你只需要过滤掉那些不该上传到服务器的字段就可以了。网络层没有分离出来
,很多人纠结于网络层属于MVC中的哪一层,因为貌似那里面没有考虑到它们的位置,因此就将大量的网络请求写到了视图控制器里,然而先不论MVC中对网络层的定位如何,网络层负责的功能就是根据请求参数获取数据,然后对数据做跟业务强相关的转换与处理,除此之外貌似也没有别的了,它的职能是明确的、单一的,也是固定的,完全可以声明一个专门处理该业务相关的网络请求的对象,传给它请求参数,它处理后返回给你想要的结果,就这么简单。视图控制器不需要去关心它里面怎么对数据进行处理与转换。视图控制器直接操作mapView
,在VC
中直接操作mapView
貌似是一件很正常的事情,然而仔细去看的话,会发现我们对mapView
的很多操作是跟业务无关的操作,比如mapView
的创建、定位到当前所在位置、设置地图层级等功能是跟业务没有什么关联的弱业务,而像添加标注Annotation
模型,因为会涉及到添加自定义的Annotation
的问题,这个是跟业务强相关的,变化的可能性是比较大的,如果要加一种新的标注类型,那么我们就要到视图控制器里,在上千行代码中找到这块功能进行修改,不便于模块测试,所以还是有必要声明一个mapView
的manager
来管理这些事情,其中弱业务可以直接写在manager
里,而那些跟业务强相关的(主要就是标注Annotation
模型的创建与添加)可以写在manager
的分类中处理。以后要加新的标注模型的话,直接到分类中进行修改就可以了。mapView的代理没有从视图控制器中剥离出来
,mapView
的代理方法有好多,如果将这些代理方法全都写到视图控制器中的话,那么势必会造成VC
中的代码过多的情况,特别是根据Annotation
返回对应的标注视图的代理方法,这个方法跟具体的业务强相关,如果你的地图中需要根据地图等级、用户身份等条件显示不同的标注视图,那么最好还是单独声明一个MapViewDelegate
类来做这个事情吧,好处就是减轻了VC
的压力,并且所有跟标注视图相关的逻辑处理都单独剥离出来,也方便你以后的维护。
再将代理从视图控制器中分离出来后,很自然的就会遇到一个问题,那就是标注View
的点击事件谁来处理,换句话说就是它的target
是谁。
如果没有剥离出MapViewDelegate
,那么我们的做法通常是将视图控制器VC
作为这些标注的target
,所有的点击事件都是视图控制器进行处理,现在既然剥离了,总不能在MapViewDelegate
中再去耦合具体的视图控制器吧,这样的话它的复用性就会降低,怎么解决这个问题呢?
声明MapViewTargetProtocol协议
在这里我们声明了一个协议来解决这个target
问题,将所有标注View
的点击方法都写在了这个协议里,并且这些方法是必须实现的方法。这样一来凡是遵循这个协议的对象都可以作为标注视图的target
,这样MapViewDelegate
中就不需要再耦合某个具体的target
了,实现了一定程度的解耦,在使用中,由于我们的点击事件不多,所以我们让视图控制器遵守了这个协议,但是如果标注的点击事件过多,那么我们就可以专门声明一个类当做这个target
,解放视图控制器。
以上就是针对地图模块的解耦方案,以下是上面提到的几个类:
-
MapSearchKeyModel
,用来管理所有数据的模型,需要被下面的类耦合。 -
MapViewManager
,用来管理mapView,并处理mapView的弱业务逻辑。 -
MapViewManager+Annotations
,分类,专门用来处理annotations标注模型。标注模型的添加与移除都在这里进行。 -
MapNetWorkManager
,负责网络请求,处理返回数据。 -
MapViewDelegate
,专门用来实现mapView的具体代理方法,所有标注视图的处理都在这里。 -
MapViewTargetProtocol
,用来解决标注视图View
的target
问题。 -
MapViewController
,视图控制器。
这样一来,我们的视图控制器中的代码就从天杀的1700行锐减到了350行,并且各模块的职责清晰,应该够它再活蹦乱跳一段时间了。
因为它不是很丑了。。。(哈哈,长得好看果然在哪里都有用)
此处应有demo。