美文网首页设计模式
利用工厂模式实现地图的一键切换

利用工厂模式实现地图的一键切换

作者: 小冰山口 | 来源:发表于2017-03-31 18:02 被阅读354次

    假设现在有一个需求:
    产品经理要在项目中集成百度地图, 过了几天又说要集成高德地图, 或者更变态的是: 同时要求两个地图都集成到项目中, 实现一键切换, 又该怎么做呢?

    我们在编程的过程中, 很重要的一点是:

    解耦!

    那么如何做到解耦呢? 请看下面这张图:

    工厂模式示意图

    大体思路是这样的

    我们订立两个协议
    • 地图协议IMapView
    #import <UIKit/UIKit.h>
    @protocol IMapView <NSObject>
    
    - (UIView *)mapView;
    
    
    - (instancetype)initWithFrame:(CGRect)frame;
    
    @end
    
    • 生产地图的协议IMapFactory
    #import "IMapView.h"
    @protocol IMapFactory <NSObject>
    
    - (id<IMapView>)mapViewWithFrame:(CGRect)frame;
    
    - (instancetype)initWithAppKey:(NSString *)appKey;
    
    @end
    
    然后根据地图协议订立地图类. 地图类继承自NSObject, 遵循IMapView协议, 实现协议方法

    以百度地图为例:

    BaiduMapView

    #import "BaiduMapView.h"
    #import <BaiduMapAPI_Base/BMKBaseComponent.h>//引入base相关所有的头文件
    #import <BaiduMapAPI_Map/BMKMapComponent.h>//引入地图功能所有的头文件
    
    @interface BaiduMapView ()
    
    @property (nonatomic,strong)UIView *mapView;
    
    @end
    
    @implementation BaiduMapView
    
    - (instancetype)initWithFrame:(CGRect)frame {
        if (self = [super init]) {
            self.mapView = [[BMKMapView alloc] initWithFrame:frame];
        }
        
        return self;
    }
    
    - (UIView *)mapView {
        return _mapView;
    }
    
    @end
    

    BaiduMapFactory

    #import "BaiduMapFactory.h"
    #import "BaiduMapView.h"
    #import <BaiduMapAPI_Base/BMKBaseComponent.h>//引入base相关所有的头文件
    #import <BaiduMapAPI_Map/BMKMapComponent.h>//引入地图功能所有的头文件
    
    
    @implementation BaiduMapFactory
    
    - (instancetype)initWithAppKey:(NSString *)appKey{
        if (self = [super init]) {
            BMKMapManager *manager = [[BMKMapManager alloc] init];
            [manager start:appKey generalDelegate:nil];
        }
        return self;
    }
    
    - (id<IMapView>)mapViewWithFrame:(CGRect)frame {
        return [[BaiduMapView alloc] initWithFrame:frame];
    }
    
    @end
    

    同样的, 高德地图也是同样的配置

    在百度地图工厂和高德地图工厂之上, 我们还需要进行一层封装, 那就是地图引擎

    MapEngine

    #import "MapEngine.h"
    #import "YFPlatform.h"
    #import "YFXMLParser.h"
    #import "IMapFactory.h"
    
    @interface MapEngine ()
    
    @property (nonatomic,strong) YFXMLParser *parser;
    @property (nonatomic,strong)id<IMapFactory> factory;
    @property (nonatomic,assign)CGRect frame;
    
    @end
    
    @implementation MapEngine
    
    static MapEngine *_instancetype = nil;
    
    + (instancetype)sharedEngine {
        return [[self alloc] init];
    }
    
    + (instancetype)allocWithZone:(struct _NSZone *)zone {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _instancetype = [super allocWithZone:zone];
            _instancetype.parser = [[YFXMLParser alloc] init];
        });
        return _instancetype;
    }
    
    - (void)start {
        for (YFPlatform *platform in self.parser.platformArray) {
            if ([platform.isOpen isEqualToString:@"YES"]) {
                self.factory = [[NSClassFromString(platform.className) alloc] initWithAppKey:platform.appKey];
            }
        }
    }
    
    - (UIView *)mapViewWithFrame:(CGRect)frame {
        self.frame = frame;
        return [[self.factory mapViewWithFrame:frame] mapView];
    }
    
    @end
    
    • 我们把地图引擎(MapEngine)设计成单例模式
    • AppDelegate代理回调中, 我们并不需导入任何关于地图的头文件, 只需要导入地图引擎, 让地图引擎帮我们完成就好
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        [[MapEngine sharedEngine] start];
        return YES;
    }
    
    • 在加载地图的控制器中, 我们只需要利用地图引擎返回一个UIView类型的对象就可以了, 控制器并不需要知道究竟是哪个地图, 因为不管是哪个地图, 都是继承自UIView的, 我们就用父类指针指向子类对象
    - (void)viewDidLoad {
        [super viewDidLoad];
        UIView *mapView = [[MapEngine sharedEngine] mapViewWithFrame:self.view.frame];
        [self.view addSubview:mapView];
    }
    
    那么地图引擎怎么知道需要加载高德地图还是百度地图呢?
    • 这里就需要用到一个xml配置文件了
      我们将地图的配置信息写成xml格式的文件
    <?xml version="1.0" encoding="UTF-8"?>
    <mapViews>
        <mapView>
            <platform Id="1" className="BaiduMapFactory" appKey="zkAQ5IKDmPUBPznesMNzxpl9HVF4HAYv" isOpen="YES">
            <platform Id="2" className="GaodeMapFactory" appKey="6d9da2c96e0b0da3b6c9cb330c5e4cf3" isOpen="NO">
        </mapView>
    </mapViews>
    
    • 然后在自定义一个xml解析器

    YFXMLParser

    #import "YFXMLParser.h"
    
    @interface YFXMLParser ()<NSXMLParserDelegate>
    
    @property (nonatomic,strong)NSXMLParser *parser;
    
    
    @end
    
    @implementation YFXMLParser
    
    - (instancetype)init {
        if (self = [super init]) {
            NSString *xmlPath = [[NSBundle mainBundle] pathForResource:@"MapInfo" ofType:@"xml"];
            self.parser = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL fileURLWithPath:xmlPath]];
            self.parser.delegate = self;
            [self.parser parse];
        }
        return self;
    }
    
    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict {
        if ([elementName isEqualToString:@"platform"]) {
            YFPlatform *platform = [YFPlatform platformWithDictionary:attributeDict];
            [self.platformArray addObject:platform];
        }
    }
    
    - (NSMutableArray<YFPlatform *> *)platformArray {
        if (!_platformArray) {
            _platformArray = [NSMutableArray array];
        }
        return _platformArray;
    }
    
    @end
    
    • xml解析器解析出来的字典再转换成模型
      模型中的属性包含了百度地图或者高德地图的所有信息, appKey, 地图工厂的类名, 以及当前是否打开等信息

    当地图引擎启动时, xml解析器会告诉引擎, 哪个地图需要打开,然后根据需要打开的地图类名初始化地图工厂

    - (void)start {
        for (YFPlatform *platform in self.parser.platformArray) {
            if ([platform.isOpen isEqualToString:@"YES"]) {
                self.factory = [[NSClassFromString(platform.className) alloc] initWithAppKey:platform.appKey];
            }
        }
    }
    

    然后页面需要拿到地图的时候, 再调用地图引擎的mapViewWithFrame:方法, 初始化完毕的地图工厂会自动生产出地图返回出去.

    - (UIView *)mapViewWithFrame:(CGRect)frame {
        self.frame = frame;
        return [[self.factory mapViewWithFrame:frame] mapView];
    }
    
    基本思路就是这样

    当我们需要切换地图时, 不用动任何代码, 只需要将配置文件的isOpen属性改一下即可

    xml配置文件

    本文Demo地址链接

    相关文章

      网友评论

        本文标题:利用工厂模式实现地图的一键切换

        本文链接:https://www.haomeiwen.com/subject/oynpottx.html