iOS设计模式-适配器

19 阅读3分钟

思想

本质是  把一个“接口不兼容”的对象,转换成你当前系统期望的接口,从而进行调用。

——将一个类的接口,转换成客户端期望的接口,使原本不兼容的类可以协同工作。

——在两个不兼容的接口之间架一座"桥",让原本无法协作的类可以一起工作,而不必修改原有代码。

可以理解为“中间翻译层”。

总结来说,适配器模式用于解决接口不兼容的问题,通过适配器将不兼容的接口转换为兼容的接口,从而使得不同的类能够一起工作。

三个角色

适配者:具有原始接口的类

抽象接口:客户端期望的接口形式

适配器:封装,持有适配者,转发调用

应用场景

常用于集成/调用 第三方 SDK、适配旧代码、数据模型适配。

“旧代码 / 第三方库 / 不同结构的数据 → 统一成我当前代码能用的样子”

应用1: 第三方 SDK的二次封装

你接入了一个第三方 SDK,它的接口不符合你当前项目的设计规范(比如命名、回调方式不同)。即使规范,原则上也不要直接依赖,直接调用其接口。

class ThirdPartyAudioSDK { 
    // 播放
    func loadAndPlay(pathString, volumeFloat) {
        print("SDK 播放: \(path) 音量: \(volume)") 
    } 
}

一般调用方式,直接依赖SDK类

class VC { 
    func do(){
        let sdk = ThirdPartyAudioSDK()
        sdk.loadAndPlay()
    }
}

采用适配器模式,对SDK进行包装

1、设计抽象协议(接口)

protocol AudioPlayer { 
    func play(fileString) 
}

2、设计适配器类

遵循协议,持有SDK对象。实质:调用转发

class AudioPlayerAdapter: AudioPlayer {
    private let sdk = ThirdPartyAudioSDK()

    func play(file: String) {
        // 转换调用:适配接口差异
        sdk.loadAndPlay(path: "/media/\(file)", volume: 1.0)
    }
}

3、业务调用

class VC {
    func playMusic(player: AudioPlayer) {
        player.play(file: "song.mp3")
    }
}

self.playMusic(playerAudioPlayerAdapter())

这样就实现了解耦,业务代码只依赖 AudioPlayer 协议,不知道内部用的是第三方 SDK。

好处:可灵活更换SDK;不污染业务代码。

应用2:服务器数据 的封装

服务端返回的JSON数据,最好不要直接读取使用。原因:

  • 避免耦合
  • 字段名不规范/适合
  • 不是所有字段都显示在UI上
  • 需要进行数据转换/预处理
{
    "num": 1,
    "is_expire": 0,
    "reward_content": "10",
    "reward_name": "WSpoint_reward",
    "reward_type": 1,
    "sign_state": 3,
    "sign_time": 0,
    "trigger_type": 1
}

解析

struct UserDTO: Decodable {
    let sign_time: String
    let trigger_type: Int
    ....
}

采用适配器模式,对服务端返回的数据进行封装

1、设计抽象协议

涵盖 业务层需要用到的数据,及 UI层可直接展示的数据

如:

protocol UIDataDisplay { 
    var count: Int { get }
    var name: String { get }
    .....
}

2、设计适配器类

class ModelAdater: UIDataDisplay {
    let json: UserDTO
    
    var num: Int
    var type: Int
    ...

    init(json: UserDTO){
        self.num = json.num
        ...
    }
    
    // 实现抽象属性
    var count: Int {
        return json.num
    }
    
    var name: String { 
        return json.firstName + json.secondName
    }
}

3、UI 与 具体数据类型 解耦

class Cell {
    func config(model: UIDataDisplay){
        self.nameLabel.text = model.name
        ...
    }
}

好处

  • 数据集中在Adapter进行处理
  • 解耦 UI 和数据结构
  • 扩展性,后端字段变了 → 只改 Adapter