swift 关联 enum 针对 Model 做类型擦除

294 阅读2分钟

问题:在 TableView 中展示多种 model

开发日常,起初 tableView 只有一个中 model,tableView 展示 [ModelType] 数组,可是日子久了,这个 ModelType 的 type 你都不知道有多少种可能,每次都要加,对于 cell 的种类也会不停地加。

怎么解决这种多 modelType 在一个list中的问题呢?

方案可能性

提供中间层封装差异 (个人因为看了好久PATs,觉得像 Protocol,类继承,Any,enum…… 都属于类型擦除,提取抽象的过程中,抽象的部分就是要做擦除的过程,至于使用什么方式因具体场景而异)

中间层用啥?

  1. protocol
  2. class 继承
  3. Any
  4. 结构中包含具有差异 model——当做成员属性,modelType 也要用 1,2方式抽象下
  5. enum

各方案的分析

针对 1,2 —— protocol,class 继承

  • 缺点a:把各种 ModelType 都有的属性再写一遍提取到中间层中
  • 缺点b:有些 property 是 model 特有的,别的 model 没有(这时有的人可能也放在中间层中当做可选型)
  • 缺点c:在 b 的前提下,这个中间层为了满足各个差异,不断地加property 而爆炸

针对 3 —— Any

  • 缺点a:漫天遍地反射代码
  • 缺点b:维护性差,同事之间改代码,迭代过程很痛苦
  • 缺点c:model 数组发生变化的时候如果只是 reload 还好,可是如果想做局部刷新引入 DifferenceKit 相关的库,怎么办 Any 不遵循 Equable

针对 4 —— 其实是删除了的,可略过,是为了引出 5 写的

protocol CaseType {}
struct ModelWrapper: CaseType {
    var model: CaseType
}

觉得哪里怪怪的,相信很多人开发过程中都想过用这种方式做擦除,不过跟 1,2 的方式好像没啥差别…… 所以 4是在浪费时间

针对 5 swift 中 enum 几乎包含了所有 struct 特性,除了没有成员属性,很多情况
但是 swift 颠覆性提供了带有关联值 enum,这让 enum 带有了擦除类型的功能类似下面的代码。对于struct,enum 免费的提供了分类与层次结构编码的效果

// 带有关联值 enum,用 struct 表示有点类似这样的代码
protocol CaseType {}
struct ModelWrapper: CaseType {
    var eraseData: CaseType
}
// 实际类表中,TableCell 的 model 层擦除
enum Cell {
    case banner(BannerModel)
    case square(User)
    case rectange(Room)
    ……
}

Cell 不需要任何 ModelWrapper 的封装方式就可以擦除 model 的差异。也满足列表局部刷新 DifferenceKit 因为 enum 的特性,Cell 还可以穷尽各种类型

  • 缺点:使用 if case 的方式读取关联值的写法不友好