Ответ Sahil Manchanda охватывает подход OOD к решению этой проблемы, но в качестве недостатка вы должны определить свои модели как класс.
Первое, что нам нужно учесть, это то, что мы обсуждаем возможность обслуживания.здесь, по моему скромному мнению, Model не должна знать о представлении (или о том, с какими представлениями оно совместимо), это ответственность Контролера.(что, если мы хотим использовать ту же модель для другого представления где-то еще?)
Во-вторых, если мы хотим абстрагировать ее до более высоких уровней, она определенно потребует понижающего / принудительного приведения в некоторыхТочка, так что есть компромисс с тем, насколько это может быть абстрагировано.
Таким образом, ради удобства обслуживания, мы можем повысить читабельность и разделение интересов / локальных рассуждений.
Я предлагаюиспользовать enum
со связанным значением для ваших моделей:
enum Row {
case animal(Animal)
case person(Person)
}
Ну, сейчас наши модели разделены, и мы можем действовать по-разному, основываясь на них.
Теперь мы должны прийтис решением для ячеек я обычно использую этот протокол в своем коде:
protocol ModelFillible where Self: UIView {
associatedtype Model
func fill(with model: Model)
}
extension ModelFillible {
func filled(with model: Model) -> Self {
self.fill(with: model)
return self
}
}
Итак, мы можем привести наши ячейки в соответствие ModelFillible
:
extension PersonCell: ModelFillible {
typealias Model = Person
func fill(with model: Person) { /* customize cell with person */ }
}
extension AnimalCell: ModelFillible {
typealias Model = Animal
func fill(with model: Animal) { /* customize cell with animal */ }
}
Прямо сейчас у нас естьсклеить их все вместе.Мы можем реорганизовать наш метод делегата tableView(_, cellForRow:_)
следующим образом:
var rows: [Row] = [.person(Person()), .animal(Animal())]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch rows[indexPath.row] {
case .person(let person): return (tableView.dequeue(for: indexPath) as PersonCell).filled(with: person)
case .animal(let animal): return (tableView.dequeue(for: indexPath) as AnimalCell).filled(with: animal)
}
}
Я верю, что в будущем это будет более читабельным / поддерживаемым, чем преобразование в нисходящий поток в представлениях или моделях.
Предложение
Я также предлагаю отделить PersonCell
от Person
и использовать его следующим образом:
extension PersonCell: ModelFillible {
struct Model {
let title: String
}
func fill(with model: Model { /* customize cell with model.title */ }
}
extension PersonCell.Model {
init(_ person: Person) { /* generate title from person */ }
}
И вваш делегат tableView использует его следующим образом:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch rows[indexPath.row] {
case .person(let person): return (tableView.dequeue(for: indexPath) as PersonCell).filled(with: .init(person))
case .animal(let animal): return (tableView.dequeue(for: indexPath) as AnimalCell).filled(with: .init(animal))
}
}
При текущем подходе компилятор всегда будет знать, что происходит, и будет защищать вас от ошибок, и в будущем, читая этот код, вы будете точно знать, что происходит.
Примечание
Причина, по которой в какой-то момент потребуется понижение / принудительное приведение, если мы попытаемся абстрагировать его до более высоких уровней (точно так же, как ответ Сахила), тот факт, что dequeue
не происходит в то же время, когда мы хотим заполнить / настроить нашу ячейку.dequeue
должен возвращать тип, известный компилятору.это либо UITableViewCell
, PersonCell
, либо AnimalCell
.В первом случае мы должны понижать его, и невозможно абстрагировать PersonCell
и AnimalCell
(если мы не попробуем понижающее / принудительное приведение в их моделях).Мы можем использовать такой тип, как GenericCell<Row>
, а также cell.fill(with: row)
, но это означает, что наша настраиваемая ячейка должна обрабатывать все случаи внутренне (она должна обрабатывать PersonCell
и AnimalCell
одновременно, что также невозможно обслуживать).
Без приведения / принудительного приведения это лучшее, что я получил за эти годы.Если вам нужно больше абстракций (одна строка для dequeue
и одна строка для fill
), ответ Сахиля - лучший путь.