TableViewをDRYな设计にリファクタリングした话
“アプリ开発の工数の8割はTableViewに使われている”と言われることもある,アプリUIの花形TableView。荒れがちなTableView周りの设计について,ペイモでのリファクタリング事例を绍介します。
Controllerイモでは,共通のViewController ・ Presenterに分岐を书いてデータや机能に差分にTableViewでも无理やり复数画面で使い回していました。开発初期はこれでもよかったのですが,机能拡张に伴い特に使いしがの界界を迎えたため,今回绍介する设计へリファクタすることとなりました。
- 同じようなデータを表示するTableViewが复数ある。
- 各TableViewごとに微妙に机能差分(検索の有无,セル选択时の挙动の违い等)がある。
- 一つのTableViewに复数种类のデータを表示する(「ユーザ情报」と「お知らせ」など)。
- や仮ータの过滤器や仮名のインデックスなど,共通化したいロジックがある。
倾向于として,ペイモではClean Architectureを采用しています。
- ViewController→演示者→UseCase→存储库
清洁建筑に则ってAPIコールします。 - 存储库→UseCase→数据模型
APIの取得结果をドメイン层の责务范囲で整形してDataModelに积み,Presenterに返却します。 - 数据模型→演示者→ViewController
Presenterは,UseCaseから返却されたDataModelからその画面の表示に必要なデータを取得してDataSourceを作成し,ViewControllerに引き渡します。
- 枚举协议。
///セル用のデータ种别
通讯协定CellRenderable {
//种别判定关数(TableViewのDelegateメソッドでの型判定に使用します)
func getType()-> CellDataType
} ///种别「A」のセル用データ
class CellTypeAData:CellRenderable {
func getType()-> CellDataType {
//自身のデータ种别を返します
返回.a
} //このデータ种别固有のプロパティ群
var hoge:字符串?
} ///种别「B」のセル用データ
class CellTypeBData:CellRenderable {
func getType()-> CellDataType {
返回.b
} //このデータ种别固有のプロパティ群
var hoge:字符串?
} ///主持人にデータを引き渡すモデル
类DataModel {
//このクラス内でデータ整形を行うのでカプセル化します
私有变量aDatas:[CellTypeAData] = []
private var bDatas:[CellTypeBData] = [] //プロトコル型で返すことで复数种类のセルがある场合も同じDataSourceとして扱えます
func getAData()-> [CellRenderable] {
//筛选などを実施
返回一个数据
} // Setterは省略
}
- PresenterはDataModelからデータを「CellRenderableプロトコル型」として取り出し,TableViewのDelegateメソッドでキャストして适切なCellClassを决定します。こうすることで,复数种类のデータを一つのTableViewのDataSourceとしてまとめることができ,ロジックを単纯化できます。
// ViewController内のTableViewのDelegateの実装部分を抜粋
func tableView(_ tableView:UITableView,cellForRowAt indexPath:IndexPath)-> UITableViewCell {//演示者からCellRenderable型で数据を取り出します
保护数据= presenter.getRow(section:indexPath.section,row:indexPath.row)else {
返回UITableViewCell()
}
// CellRenderable#getTypeで种别を特定してキャストします
切换data.getType(){
案例.a:
让typeAData =数据为! CellTypeAData
//省略
案例.b:
让typeBData =数据为! CellTypeBData
//省略
}
}
- にしておくイン层までをDRYにしておくと,メンテナンス性が确保できて改修コストが抑えられます。
- 复数のデータ种别があっても枚举,Protocolで整理してやると取り回しよく実装できます。