З яких причин ви б використовували окреме розширення класу для кожного делегата в Swift?


13

Я працював підручником Рея Вендерліха і зауважив, що автор використовує розширення класу, щоб утримувати зворотні делегатські виклики, а не обробляти їх у самому класі, тобто:

делегувати зворотні виклики всередині розширення класу:

extension LogsViewController : UIPopoverPresentationControllerDelegate {
    func adaptivePresentationStyleForPresentationController(controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
        ...
    }
}

на відміну від того, щоб він містився у класі:

делегувати зворотні виклики всередині класу:

class LogsViewController : UITableViewController, UIPopoverPresentationControllerDelegate {
    func adaptivePresentationStyleForPresentationController(controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
        ...
    }
}

Мені це здалося дивним і цікавим водночас. У нього є файл, призначений саме для розширень класу LogsViewController під назвою "LogsViewControllerExtension.swift" і має різне розширення для кожного протоколу делегата: UITableViewDataSource, UISplitViewDelegate тощо, тобто:

кілька розширень класу кожен w / делегувати зворотні дзвінки всередині власного файлу:

extension LogsViewController: UISplitViewControllerDelegate {
    ... callbacks
}

extension LogsViewController : UIPopoverPresentationControllerDelegate {
    ... callbacks
}

Чому?

Які переваги є для цього? Я можу побачити, де це може бути трохи легше читати, щоб розділити це, але в той же час це рівень непрямості. Чи існують принципи ОО, які підтримують чи проти цього?


1
Ви можете написати багато такої структури, яка принципово підтримується принципами ОО, але все-таки винна в перенапруженні.
Роберт Харві

1
@RobertHarvey вірно, але ви маєте на увазі, що приклад коду, який я тут показав, є формою переобладнання? Делегування є загальною моделлю в розробці iOS. Також ви використовуєте розширення класу чи не змінюєте код всередині нього, тож це буде більше схоже на реструктуризацію коду, а не на повторну інженерію
morbidhawk

1
Я бачу цю схему вкидання в один файл купу розширень, і я не знаю, звідки це береться. Здається, деякі люди вже зловживають цим і просто довільно кидають кожен шматочок коду в розширення в одному файлі. Я впевнений, що для цього є поважна причина, але мені здається, що деякі програмісти просто роблять це, не розуміючи, чому. Я продовжую шукати перевагу цього над використанням MARK: - і хотів би знайти якесь остаточне джерело того, чому розширення слід використовувати таким чином.
Девід Ларі

Відповіді:


15

Я не знаю, чому ви сказали, що це додає рівня непрямості. Можливо, ви маєте на увазі щось інше, ніж традиційне значення, бо немає ніякого додаткового опосередкованого дії, створеного цим шляхом. Але навіщо це робити?

Я роблю це, тому що він більш модульний. Весь код, необхідний інтерфейсу, згрупований в одному місці (крім фактичних властивостей.) Якщо я згодом вирішу зробити окремий клас для реалізації цього протоколу (і таким чином ввести фактичний рівень непрямості), все, що мені потрібно робити - це змінити розширення на власний клас (передаючи потрібні властивості через функцію init,) та створити властивість у ViewController для інстанціювання об'єкта.

Я також розміщую в розширенні будь-які приватні функції, які використовуються лише функціями цього протоколу. Я не пішов так далеко, щоб створити цілком окремий файл для розширення, але, роблячи це, ясно, що ці приватні функції призначені лише для цього протоколу.

І в будь-якому випадку, люди часто скаржаться на жирові контролери, і таким чином порушувати контролер перегляду, це допомагає краще організувати його, навіть якщо це насправді не робить контролер зору тоншим.


Я бачу, що ви маєте на увазі під модульністю. Як тільки я зрозумів, що відбувається, це виглядало як чистий спосіб розділення проблем. Я думаю, що рівень непрямості є новим набором очей (і я упереджено виходить із шляху Obj-c). Я відчуваю той самий рівень непрямості підкласифікації, де посилається код, який визначений десь в базовому класі, і його трохи складніше знайти. Я погоджуюся, що організаційно це додає переваги (з невеликою непрямою вартістю)
morbidhawk

Я думаю, що це робилося раніше з категоріями класів в Obj-C. Я ніколи не був прихильником цих, але я думаю, що їм потрібен окремий файл. Оскільки зараз у Swift ви можете зберегти розширення в тому самому файлі, саме це я, мабуть, зроблю, якщо вирішу їх розбити таким чином
morbidhawk

2

Як сказав Даніель щодо непрямості, рівня його немає.
Я погоджуюся з ним, і я хотів би додати додаткову потужність розширення протоколу, про яку я нещодавно знав.

Скажіть, що у вас є протокол, didCopyTextнаприклад. Ви реалізуєте це як:

protocol didCopyText {
  var charachtersCount: Int {get}
  func addToClipboardWith(text: String)
}

У Swift властивості та методи не реалізовані в декларації протоколу, ви б хотіли записати реалізацію в кожному класі, який відповідає цьому didCopyText, причому додаткове число класів, що відповідають цьому протоколу з тією ж реалізацією, закінчилося б просто безладним. повторний код. Ось тут корисні розширення протоколів.

protocol didCopyText {
var charachtersCount: Int {
    get {
     // implementation
    }
}
func addToClipboardWith(text: String) {
      // implementation
 }
}

З реалізацією властивостей та методів протоколу. Тепер будь-який клас, відповідний цьому протоколу, буде використовувати ту саму реалізацію.


дякую, що поділилися цим. У той час, коли я задавав це питання, я не розумів про деякі недоліки спадкування OO, і те, що ви тут описуєте, - це чудовий спосіб отримати ті самі функції, які використовуються всіма класами-реалізаторами, які відповідають цьому інтерфейсу, а не примушувати їх успадковувати все від предмета. І якщо ви дотримуєтесь принципу розбиття інтерфейсу, ви можете розбити протоколи за необхідності, щоб у класів реалізації ніколи не було властивостей / методів інтерфейсу, які їм не потрібні.
morbidhawk

1

Скажімо, ваш клас підтримує три протоколи, і тому вам доведеться додати три набори функцій. Єдина мета цих функцій - підтримка протоколу, тому вам потрібна деяка документація.

Однак якщо ви додасте розширення для кожного протоколу, і в кожному розширенні ви реалізуєте саме ті функції, які необхідні для цього одного протоколу, це зробить ваш код набагато більш читабельним.

Я, швидше за все, не ставлю їх в окремі файли, якщо ці розширення справді великі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.