iOS适配器设计模式

作者: idage | 来源:发表于2016-09-24 16:10 被阅读1590次

    (-) 提出问题

    朋友们在开发中有没有遇到过这种情况:开发中写了一个视图控件,虽然这个控件只是一个展示类的,并没有什么交互。但是在项目好几个地方都用到了这个控件了,你在给这个视图控件负值的时候是怎么做的呢?
    是不是这么写的呢?

    -(void)laodData:(ItemModel*)model ;
    

    这么写没有错,而且感觉很简单。但是有两点不好:

    1. 视图根数据模型有耦合,视图类引入了模型。

    2.当你在项目里其他的地方用到了这个视图类,而且对应的模型不再是ItemModel了,而是一个新的模型,比如说ContentModel,这个时候你要再写一个初始化方法吗?类似:

    -(void)laodData:(ContentModel*)model ;
    

    这样写似乎也没有错,但是如多更多地方用了怎么办?
    你有没有想过,我不同的地方用到一个控件,为什么要改写好的视图控件呢?有没有别的方法能让我写的视图控件独立出来,不和数据模型产生耦合呢?这就用到了设计模式中的一个适配器模式了。

    (二 )适配器设计模式 简介

    将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
    适用场景:
    1、已经存在的类的接口不符合我们的需求;
    2、创建一个可以复用的类,使得该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作;
    3、在不对每一个都进行子类化以匹配它们的接口的情况下,使用一些已经存在的子类。

    (三 )使用适配器模式重构代码

    下面从一个例子中看下适配器的写法:

    项目中有一个视图类,展示一个效果,前面是一个图片,图片的后面是一段文字,这个控件在项目里多处使用,并且对应不同的数据模型:效果如下


    屏幕快照 2016-09-24 下午4.02.01.png
    第1步 修改视图接收数据方法

    把视图控件的初始化数据改为接收一个遵守某个协议的数据

    -(void)loadData:(id  <ContentViewAdapterProtocol>)data{
        self.image      = [data image];
        self.contentStr = [data contentStr];
    }
    

    不管传过来的数据是什么类型,只要服从这个协议,实现协议里的方法,视图就能处理这个数据了

    第2步 实现这个协议

    新建也个协议文件

    @protocol ContentViewAdapterProtocol <NSObject>
    
    -(UIImage*)image;
    
    -(NSString*)contentStr;
    
    @end
    
    第3步 创建根适配器类

    类遵守上面的协议,并实现协议的方法,但是只是空实现

    -(UIImage*)image{
        
        return nil;
    }
    
    -(NSString*)contentStr{
        
         return nil;
    }
    

    添加初始化数据方法

    - (instancetype)initWithData:(id)data;
    

    这样这个适配器就完成了。

    第4步 针对不同的类创建适配器

    使用适配器的时候分为类适配器和对象适配器,类适配器是针对每一个数据类创建一个适配器。创建的这个适配器类继承于上面的那个根适配器类。如果要适配另一个数据类就需要再新建一个新的适配器类来适配。
    新建真对ItemModel的适配器:

    #import "ItemModelAdeapter.h"
    #import "ItemModel.h"
    @implementation ItemModelAdeapter
    - (instancetype)initWithData:(id)data{
        self = [super init];
        if (self) {
            
            self.data = data;
            
        }
        
        return self;
    }
    -(UIImage*)image{
    
        ItemModel *model =  self.data;
    
        return model.image;
    }
    
    -(NSString*)contentStr{
        ItemModel *model =  self.data;
        
        return model.conntentStr;
    
    }
    

    对象适配器是只创建一个适配器类,继承于上面的根适配器。然后根据不同的数据类返回不同的数据:

    #import "ModelAdapter.h"
    #import "ItemModel.h"
    #import "ContenModel.h"
    //对象适配器
    @implementation ModelAdapter
    - (instancetype)initWithData:(id)data{
        self = [super init];
        if (self) {
            
            self.data = data;
            
        }
        
        return self;
    }
    -(UIImage*)image{
        
        if ([self.data isMemberOfClass:[ContenModel class]]) {
    
        ContenModel *model =  self.data;
            
        return [UIImage imageNamed:model.imageName];
        
        }else{
            
            ItemModel *model =  self.data;
            
            return model.image;
    
        }
    }
    
    -(NSString*)contentStr{
       
        if ([self.data isMemberOfClass:[ContenModel class]]) {
            
            ContenModel *model =  self.data;
            
            return model.conntentStr;
            
        }else{
            
            ItemModel *model =  self.data;
            
            return model.conntentStr;
            
        }
    }
    

    对象适配器的好处是在一个类里处理不同的数据,类会少一些。但是如果要适配的类太多就会显得很复杂,而对象适配器适配方法分散,类比较多。

    第5步 使用适配器

    类适配器 适配两个类

      第一个
        ContenModel *contenModel = [[ContenModel alloc]init];
        contenModel.conntentStr  =  @"时间:10:32:12";
        contenModel.imageName    =  @"shijian";
    
        ContentViewAdapter *modelAdapter = [[ContentModelAdeapter alloc]initWithData:contenModel];
        
        ContentView *contentView = [[ContentView alloc]initWithFrame:CGRectMake(100, 100, 200, 20)];
        [contentView loadData:modelAdapter];
        [self.view addSubview:contentView];
    第二个
     ItemModel *itemModel  = [[ItemModel alloc]init];
        itemModel.conntentStr =  @"心率:100次";
        itemModel.image       =  [UIImage imageNamed:@"mapHeaderIcon"];
        
        ContentViewAdapter *modelAdapter1 = [[ItemModelAdeapter alloc]initWithData:itemModel];
        
        ContentView *contentView1 = [[ContentView alloc]initWithFrame:CGRectMake(100, 200, 200, 20)];
        [contentView1 loadData:modelAdapter1];
        [self.view addSubview:contentView1];
    

    对象适配器

    第一个
    ItemModel *itemModel1  = [[ItemModel alloc]init];
       itemModel1.conntentStr =  @"心率:100次";
       itemModel1.image       =  [UIImage imageNamed:@"mapHeaderIcon"];
       
       ContentViewAdapter *modelAdapter2 = [[ModelAdapter alloc]initWithData:itemModel];
       
       ContentView *contentView2 = [[ContentView alloc]initWithFrame:CGRectMake(100, 300, 200, 20)];
       [contentView2 loadData:modelAdapter2];
       [self.view addSubview:contentView2];
    第二个
    ContenModel *contenModel2 = [[ContenModel alloc]init];
       contenModel2.conntentStr  =  @"时间:10:32:12";
       contenModel2.imageName    =  @"shijian";
       
       ContentViewAdapter *modelAdapter3 = [[ModelAdapter alloc]initWithData:contenModel];
       
       ContentView *contentView3 = [[ContentView alloc]initWithFrame:CGRectMake(100, 400, 200, 20)];
       [contentView3 loadData:modelAdapter3];
       [self.view addSubview:contentView3];
    

    (四)适配器模式的缺点

    优点:

    解耦合,让视图类不合数据类产生耦合,使视图类更加独立。 新增加数据类的时候不需要修改视图类。

    缺点:

    会新增加很多类,使系统更凌乱,代码可读性更弱了。
      
    所以大家酌情使用

    (五)鸣谢

    这篇文章是看过 YouXianMing老师 的教程后,得到的启发并重构自己项目代码后写的总结。希望对大家有点帮助。在此感谢YouXianMing老师。

    代码github下载地址

    iOS策略模式

    相关文章

      网友评论

      • 萝卜的萝卜:这个不是桥接模式吗
      • cloud_sky_z:看了好几篇文章,觉得这篇写的最清晰
      • 董二千:这种问题用适配器模式解决不算好的实践,protocol就可以了,或者用wrapper。文章主要是阐述模式思想,为了设计思想而造🌰
      • 新地球说着一口陌生腔调:作者错别字有点多,是遵循协议 不是尊重。还有其他…
        idage:@新地球说着一口陌生腔调 哪里写尊重了……
        idage:不好意思,写的时候时间有点晚了,检查的没那么仔细
      • bigParis:怎么说呢,把适配器模式用在view上有些过度设计的意思,想一下,为什么view会传来不同的数据模型,要什么传什么就好了,在外面组装好再给view不行吗?
        idage:@bigParis 传给view的是适配器,不是直接传的模型,模型数据是在适配器类里处理组装好了再由view去用的,我不能说这是组装好了再传给view????? 而且我在文章里也说了这个模式的缺点了啊:grin:
        bigParis: @idage 你这个不是组装额,直接把id类型的model传给view了,但实际view要显示的数据都是确定的,数据模型也就确定了,所以传的应该是确定的类型,要是用adapter去适配不同的模型有点设计过度了
        idage:@bigParis 我写的这个不是在外面适配好了穿过去的吗?这个view只是例子,为了说明这个模式的思想而已
      • minjing_lin:挺不错的,mark
      • lhq_cd:不错哟,学习了
      • 李国安:我有一个问题, 为什么要创建适配器类呢? 比如封装一个控件, 该控件需要一个数据模型, 如果指定数据模型例如:MLModel ,这样的话就会出现最开始你描述的问题,就是该控件很多地方需要用到的时候就蛋疼了! 但是如果为该控件声明一个协议, 只要遵守该协议的任意对象都可以作为数据模型,这不就OK了么? 数据模型对象自己实现协议就好了啊, 所以我不是很明白为什么还要专门创建一个适配器来实现协议,这一点我没明白!
        李国安:@idage :+1::+1::+1:完美的🌰
        idage:@Lemon龙 设计模式顾名思义是一种模式,规定了处理问题的一个思路,比如一个手机在中国和美国发售,中国电压是220v,美国是380v,为了在两国都能用,只要设计两种充电器就好了(两个适配器),没有必要改手机(视图控件)和电源(数据模型)!

      本文标题:iOS适配器设计模式

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