适配器模式(转)

(-) 提出问题

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

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

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

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

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

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

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

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

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

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

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

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

第1步 实新建一个协议

新建一个协议文件,协议名为 ContentViewAdapterProtocol,这个协议的方式是视图需要显示的数据:

@protocol ContentViewAdapterProtocol <NSObject>

-(UIImage*)image;

-(NSString*)contentStr;

@end
 

第2步 修改视图接收数据方法

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

.h 文件如下:

/**
 *  添加符合ContentViewAdapterProtocol协议的数据类
 */
-(void)loadData:(id<ContentViewAdapterProtocol>)data;


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

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

第3步 创建根适配器类

新建一个根适配器类:ContentViewAdapter, 类遵守上面的协议 ContentViewAdapterProtocol,添加初始化数据方法,并实现协议的方法,但是只是空实现:

.h 文件:
#import <Foundation/Foundation.h>
#import "ContentViewAdapterProtocol.h"
@interface ContentViewAdapter : NSObject<ContentViewAdapterProtocol>
/**
 *  输入对象
 */
@property (nonatomic, weak) id data;
/**
 *  与输入对象建立联系
 *  @param data 输入的对象
 *  @return 实例对象
 */
- (instancetype)initWithData:(id )data;

@end


.m 文件:

- (instancetype)initWithData:(id)data{
    self = [super init];
    if (self) {
        self.data = data;
    }
    return self;
}

-(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];

(四)适配器模式的缺点

优点:

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

缺点:

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

参考链接:https://www.jianshu.com/p/61ade7936c93

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
适配器模式是一种结构型设计模式,它允许将不兼容的接口换为可兼容的接口。在Java中,适配器模式通常用于将一个类的接口换为另一个类的接口,以便这两个类可以相互工作。 例如,假设你有一个欧洲的插座,但你需要将它连接到一个中国的插座上。这时,你可以使用一个适配器来换欧洲插座的接口,使它与中国插座兼容。在Java中,这个适配器可以是一个实现中国插座接口的类,它包含一个欧洲插座对象,并将欧洲插座的接口换为中国插座的接口。 下面是一个简单的Java代码示例,演示了如何使用适配器模式将欧洲插座换为中国插座: // 欧洲插座接口 public interface EuropeanSocket { public void powerOn(); } // 欧洲插座实现类 public class EuropeanSocketImpl implements EuropeanSocket { public void powerOn() { System.out.println("欧洲插座已通电"); } } // 中国插座接口 public interface ChineseSocket { public void connect(); } // 中国插座适配器 public class ChineseSocketAdapter implements ChineseSocket { private EuropeanSocket europeanSocket; public ChineseSocketAdapter(EuropeanSocket europeanSocket) { this.europeanSocket = europeanSocket; } public void connect() { europeanSocket.powerOn(); System.out.println("插入中国插座,电器已连接"); } } // 测试类 public class AdapterTest { public static void main(String[] args) { EuropeanSocket europeanSocket = new EuropeanSocketImpl(); ChineseSocket chineseSocket = new ChineseSocketAdapter(europeanSocket); chineseSocket.connect(); } } 在这个例子中,EuropeanSocket是欧洲插座的接口,EuropeanSocketImpl是欧洲插座的实现类。ChineseSocket是中国插座的接口,ChineseSocketAdapter是中国插座的适配器,它将欧洲插座的接口换为中国插座的接口。在AdapterTest中,我们创建了一个EuropeanSocketImpl对象,并将它传递给了ChineseSocketAdapter的构造函数。然后,我们调用了ChineseSocket的connect()方法,该方法通过适配器将欧洲插座的接口换为中国插座的接口,并连接了电器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值