Z jakich powodów używałbyś osobnego rozszerzenia klasy dla każdego delegata w Swift?

13

Pracowałem przez samouczek Raya Wenderlicha i zauważyłem, że autor używa rozszerzeń klas do przechowywania wywołań zwrotnych delegatów zamiast obsługi ich w samej klasie, tj .:

delegować wywołania zwrotne wewnątrz rozszerzenia klasy:

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

w przeciwieństwie do umieszczenia go w klasie:

delegować wywołania zwrotne w klasie:

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

Uznałem to za dziwne i jednocześnie interesujące. Ma plik dedykowany tylko rozszerzeniom klasy LogsViewController o nazwie „LogsViewControllerExtension.swift” i ma inne rozszerzenie dla każdego protokołu delegowanego: UITableViewDataSource, UISplitViewDelegate itp. Tj .:

wiele rozszerzeń klas, każde z wywołaniami zwrotnymi delegatów wewnątrz własnego pliku:

extension LogsViewController: UISplitViewControllerDelegate {
    ... callbacks
}

extension LogsViewController : UIPopoverPresentationControllerDelegate {
    ... callbacks
}

Dlaczego?

Jakie są zalety tego działania? Widzę, gdzie może być trochę bardziej czytelne, aby to oddzielić, ale jednocześnie jest to poziom pośredni. Czy istnieją zasady OO, które wspierają lub nie robią tego?

morbidhawk
źródło
1
Możesz napisać wiele takich struktur, które są zasadniczo wspierane przez zasady OO, ale nadal są winne nadmiernej inżynierii.
Robert Harvey
1
@RobertHarvey prawda, ale czy sugerujesz, że przykładowy kod, który tu pokazałem, jest formą nadinżynierii? Delegowanie jest powszechnym wzorcem w rozwoju iOS. Również to, czy używasz rozszerzeń klas, czy nie, nie zmienia kodu w nim zawartego, więc bardziej przypominałoby to restrukturyzację kodu niż ponowne (/ ponad) projektowanie
morbidhawk
1
Widzę ten schemat wrzucania wielu rozszerzeń do tego samego pliku i nie wiem, skąd to pochodzi. Wygląda na to, że niektórzy już go nadużywają i po prostu arbitralnie wrzucają każdy kawałek kodu do rozszerzenia w tym samym pliku. Jestem pewien, że jest dobry powód, aby robić to czasami, ale mam wrażenie, że niektórzy programiści robią to po prostu nie rozumiejąc, dlaczego. Ciągle szukam przewagi nad korzystaniem z MARK: - i chciałbym znaleźć jakieś ostateczne źródło, dlaczego rozszerzenia powinny być używane w ten sposób.
David Lari,

Odpowiedzi:

15

Nie wiem, dlaczego powiedziałeś, że dodaje poziom pośredni. Może masz przez to na myśli coś innego niż tradycyjne znaczenie, ponieważ dzięki temu nie powstaje żadna dodatkowa pośrednictwo. Ale dlaczego to robisz?

Robię to, ponieważ jest bardziej modułowy. Cały kod wymagany przez interfejs jest pogrupowany w jednym miejscu (z wyjątkiem faktycznych właściwości.) Jeśli później zdecyduję się stworzyć osobną klasę do wdrożenia tego protokołu (a tym samym wprowadzić rzeczywisty poziom pośredni), wszystko, czego potrzebuję zrobić to zmienić rozszerzenie na własną klasę (przekazując potrzebne właściwości za pomocą funkcji init) i utworzyć właściwość w ViewController, aby utworzyć instancję obiektu.

W rozszerzeniu umieściłem również funkcje prywatne, które są używane tylko przez funkcje tego protokołu. Nie posunąłem się tak daleko, aby utworzyć całkowicie oddzielny plik dla rozszerzenia, ale czyni to jasne, że te prywatne funkcje są tylko dla tego protokołu.

W każdym razie ludzie często narzekają na kontrolery fat view, a rozbicie kontrolera view w ten sposób pomaga lepiej go zorganizować, nawet jeśli tak naprawdę nie cieńszy kontrolera view.

Daniel T.
źródło
Rozumiem, co masz na myśli przez modułowość. Kiedy zrozumiałem, co się dzieje, wyglądało to na czysty sposób na rozdzielenie obaw. Poziom pośredni, jak sądzę, dotyczy nowego zestawu oczu (i jestem tendencyjny, wychodząc z Obj-c). Czuję ten sam poziom pośrednictwa, gdy podklasowanie, do którego odwołuje się kod, jest zdefiniowane gdzieś indziej w klasie bazowej i trochę trudniej go znaleźć. Zgadzam się, że pod względem organizacyjnym przynosi korzyść (przy niewielkich kosztach pośrednich)
morbidhawk
Wydaje mi się, że zrobiono to wcześniej z kategoriami klas w Obj-C. Nigdy nie byłem ich wielkim fanem, ale myślę, że wymagały osobnego pliku. Od teraz w Swift możesz przechowywać rozszerzenie w tym samym pliku, co prawdopodobnie zrobię, jeśli zdecyduję się je w ten sposób
rozbić
2

Jak powiedział Daniel w odniesieniu do pośrednictwa, nie ma jego poziomu.
Zgadzam się z nim i chciałbym dodać dodatkową potężną funkcję rozszerzeń protokołu, którą znałem ostatnio.

Powiedz, że masz didCopyTextna przykład protokół . Zaimplementujesz to jako:

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

W Swift właściwości i metody nie są zaimplementowane w deklaracji protokołu, chciałbyś napisać implementację w każdej klasie didCopyTextzgodnej z, przy przyrostowej liczbie klas zgodnych z tym protokołem z tą samą implementacją, skończyłoby się to tylko bałaganem powtarzający się kod. Przydają się rozszerzenia protokołu.

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

Dzięki implementacji właściwości i metod protokołu. Teraz każda klasa jest zgodna z tym protokołem i będzie korzystać z tej samej implementacji.

MEnnabah
źródło
dzięki za udostępnienie tego. Kiedy zadawałem to pytanie, nie zdawałem sobie sprawy z niektórych wad dziedziczenia OO, a to, co tu opisujesz, jest świetnym sposobem na ponowne wykorzystanie tych samych funkcji przez wszystkie klasy implementacyjne, które są zgodne z tym interfejsem, zamiast być zmuszonym do odziedziczyć wszystko po obiekcie. Jeśli zastosujesz się do zasady segregacji interfejsów, możesz rozdzielić protokoły według potrzeb, aby zapewnić, że klasy implementujące nigdy nie będą miały właściwości / metod z interfejsu, których nie potrzebują.
morbidhawk
1

Powiedzmy, że twoja klasa obsługuje trzy protokoły i dlatego musisz dodać trzy zestawy funkcji. Jedynym celem tych funkcji jest obsługa protokołu, dlatego potrzebujesz trochę dokumentacji.

Jeśli jednak dodasz rozszerzenie dla każdego protokołu, a dla każdego rozszerzenia zaimplementujesz dokładnie funkcje potrzebne dla tego jednego protokołu, dzięki czemu Twój kod będzie znacznie bardziej czytelny.

Najprawdopodobniej nie umieściłbym ich w osobnych plikach, chyba że te rozszerzenia są naprawdę duże.

gnasher729
źródło