举例提问
你有什么方法在项目中实现快速切换百度地图
和高德地图
?(ps:本文例子是针对iOS项目讲解的,但是思想是可以借鉴到其他语言中的。)
或许有不少朋友的答案是修改代码呗。
那么,我再给一个前提:如何在不改动客户端代码或者尽可能少量的修改代码呢?
今天,我就带大家来实现如何不改动客户端代码或者尽可能少量的修改客户端代码来实现项目中百度地图
和高德地图
的快速切换。
集成百度地图
先创建一个工程,然后将百度地图sdk
集成到项目,(此处忽略集成步骤,具体可以在百度地图sdk接入文档
中查看)。在百度地图官网申请appkey,然后在AppDelegate
初始化百度地图,这篇文章我们只是简单的使用一下百度地图,在ViewController
中我们导入#import <BaiduMapAPI_Map/BMKMapComponent.h>
,然后创建一个mapView
添加到控制器上,
#import "ViewController.h"
#import <BaiduMapAPI_Map/BMKMapComponent.h>
@interface ViewController ()
@property (nonatomic, strong) BMKMapView *mapView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.mapView = [[BMKMapView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:self.mapView];
}
运行我们的app,ok,不出意外的话,我们界面上能够正确的显示百度地图。(ps:问了项目结构看起来舒服,这里分文件夹管理Framework
下包含了GaodeMap
和BaiduMap
两个文件夹)
集成高度地图
然后我们需要再集成高德地图sdk
到项目,申请appkey
,然后在Appdelegate
里面添加
[AMapServices sharedServices].apiKey = @"你的key";
同样,我们在ViewController
中我们导入#import <MAMapKit/MAMapView.h>
,然后创建一个mapView
添加到控制器上,
#import "ViewController.h"
// #import <BaiduMapAPI_Map/BMKMapComponent.h>
#import <MAMapKit/MAMapView.h>
@interface ViewController ()
// @property (nonatomic, strong) BMKMapView *mapView;
@property (nonatomic, strong) MAMapView *mMapView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// self.mapView = [[BMKMapView alloc] initWithFrame:self.view.bounds];
// [self.view addSubview:self.mapView];
self.mMapView = [[MAMapView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:self.mMapView];
}
运行我们的代码,ok,我们也能愉快的看到界面上显示的是高德地图。
面向协议思想
通过以上的代码,我们可以发现有两个点:
- 无论是
BMKMapView
还是MAMapView
,他们都是继承于UIView
的。 - 必须指定创建的位置和大小。
因此,我们可以通过protocol
来抽出他们的共性。
ok,我们在Framework文件夹
下创建一个BaseMap文件夹
,然后创建一个IMapViewProtocol
的协议类,然后将上述的两个特点定义成两个方法。
#import <UIKit/UIKit.h>
@protocol IMapViewProtocol <NSObject>
- (instancetype)initWithFrame:(CGRect)frame;
- (UIView *)getMapView;
@end
接下来,我们创建两个类BaiduMapView
和GaodeMapView
并且这两个类都实现IMapViewProtocol
协议,因此需要实现协议方法。
BaiduMapView
#import "BaiduMapView.h"
#import <BaiduMapAPI_Map/BMKMapComponent.h>
@interface BaiduMapView ()
@property (nonatomic, strong) BMKMapView *mapView;
@end
@implementation BaiduMapView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super init];
if (self) {
_mapView = [[BMKMapView alloc] initWithFrame:frame];
}
return self;
}
- (UIView *)getMapView {
return _mapView;
}
@end
GaodeMapView
#import "GaodeMapView.h"
#import <MAMapKit/MAMapView.h>
@interface GaodeMapView ()
@property (nonatomic, strong) MAMapView *mapView;
@end
@implementation GaodeMapView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super init];
if (self) {
_mapView = [[MAMapView alloc] initWithFrame:frame];
}
return self;
}
- (UIView *)getMapView {
return self.mapView;
}
@end
此时我们可以在ViewController
里面添加如下代码。
#import "ViewController.h"
#import "BaiduMapView.h"
#import "GaodeMapView.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
id<IMapViewProtocol> mapView = [[GaodeMapView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:[mapView getMapView]];
}
运行项目,此时,我们可以看到界面上显示出来高德地图。(ps:在Appdelegate
里面配置好高德地图的key,下面也是同理。)此时,如果我们想要切换成百度地图的时候,我们只需要修改创建mapView
对象的类
。
工厂模式
到现在为止,我们确实能够尽可能少地修改代码去切换两个地图的显示了,可是,
假如我们一个项目中很多的地方用到了百度地图或者高德地图,那么我们是不是需要逐个逐个地去修改呢?这时候,我们就应该考虑使用工厂模式了,就好比生产衣服的工厂,怎么生产的我们不知道,其实我们也并不关心,我们只对其生产出来的结果感兴趣。那么,我们应该将生产高德地图的工厂和生产百度地图的工厂分开呢,还是写在一个工厂呢?不用想,我们肯定是需要分开生产的。但是,我们可以考虑到两个工厂生产出来的实例有个共同点:
- 生产出来的实例必须遵守
<IMapViewProtocol>
协议
因此,我们可以定义一个协议类IMapFactoryProtocol
:
#import <UIKit/UIKit.h>
#import "IMapViewProtocol.h"
@protocol IMapFactoryProtocol <NSObject>
- (id<IMapViewProtocol>)getMapView:(CGRect)frame;
@end
然后定义两个遵守<IMapViewProtocol>
的工厂类:
BaiduMapFactory
#import "BaiduMapFactory.h"
#import "BaiduMapView.h"
#import <BaiduMapAPI_Base/BMKMapManager.h>
@interface BaiduMapFactory (){
BMKMapManager *_mapManager;
}
@end
@implementation BaiduMapFactory
- (instancetype)init {
self = [super init];
if (self) {
_mapManager = [[BMKMapManager alloc]init];
// 如果要关注网络及授权验证事件,请设定 generalDelegate参数
BOOL ret = [_mapManager start:@"xnbikl7G1GolppT27HKdgfd10jtqDd0G" generalDelegate:nil];
if (!ret) {
NSLog(@"初始化失败");
}
}
return self;
}
- (id<IMapViewProtocol>)getMapView:(CGRect)frame {
return [[BaiduMapView alloc] initWithFrame:frame];
}
@end
GaodeMapFactory
#import "GaodeMapFactory.h"
#import <AMapFoundationKit/AMapFoundationKit.h>
#import "GaodeMapView.h"
@implementation GaodeMapFactory
- (instancetype)init {
self = [super init];
if (self) {
[AMapServices sharedServices].apiKey = @"fdbbc0bd9dc04f6b9922bf357ef1eaa8";
}
return self;
}
- (id<IMapViewProtocol>)getMapView:(CGRect)frame {
return [[GaodeMapView alloc] initWithFrame:frame];
}
经过上面的结构整理,我们在定义一个引擎类----MapEngine
:
MapEngine.h
:
#import <Foundation/Foundation.h>
#import "IMapFactoryProtocol.h"
@interface MapEngine : NSObject
- (id<IMapFactoryProtocol>)getMapFactory;
@end
MapEngine.m
:
#import "MapEngine.h"
#import "BaiduMapFactory.h"
#import "GaodeMapFactory.h"
@implementation MapEngine
- (id<IMapFactoryProtocol>)getMapFactory {
return [[BaiduMapFactory alloc] init];
}
@end
好了,到此为止,我们的结构优化算是完成了。这时候,我们再回到ViewController
中来看看我们的代码。
#import "ViewController.h"
#import "IMapViewProtocol.h"
#import "MapEngine.h"
@interface ViewController ()
@property (nonatomic, strong) UIView *mMapView;
@property (nonatomic, strong) MapEngine *engine;
@property (nonatomic, strong) id<IMapFactoryProtocol> factory;
@property (nonatomic, strong) id<IMapViewProtocol> mapView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.engine = [[MapEngine alloc] init];
self.factory = [self.engine getMapFactory];
self.mapView = [self.factory getMapView:self.view.bounds];
self.mMapView = [self.mapView getMapView];
[self.view addSubview:self.mMapView];
}
现在,我们如果想要切换地图显示,我们只需要在MapEngine
类里面改变一下工厂类即可。这时候,我们甚至还可以接入服务器,让服务配合,可以不修改客户端任何代码就实现地图切换了。
结尾
- 如果您有更好的思路欢迎交流。
- 如果您有发现错误或者有疑问的地方,欢迎交流,谢谢!!
- 大神勿喷!!