2016-11-23 63 views
5

Wprowadzam wzór gościa w Swift 2.2 dla projektu w pracy.W jaki sposób mogę zredukować plan podstawowy w systemie Swift?

Aby nie musieć sprowadzać kodu źródłowego i zaoszczędzić mi trochę czasu, użyję numeru example of visitor pattern in swift by Oktawian Chojnacki.

protocol PlanetVisitor { 
    func visit(planet: PlanetAlderaan) 
    func visit(planet: PlanetCoruscant) 
    func visit(planet: PlanetTatooine) 
} 

protocol Planet { 
    func accept(visitor: PlanetVisitor) 
} 

class PlanetAlderaan: Planet { 
    func accept(visitor: PlanetVisitor) { visitor.visit(self) } 
} 
class PlanetCoruscant: Planet { 
    func accept(visitor: PlanetVisitor) { visitor.visit(self) } 
} 
class PlanetTatooine: Planet { 
    func accept(visitor: PlanetVisitor) { visitor.visit(self) } 
} 

class NameVisitor: PlanetVisitor { 
    var name = "" 

    func visit(planet: PlanetAlderaan) { name = "Alderaan" } 
    func visit(planet: PlanetCoruscant) { name = "Coruscant" } 
    func visit(planet: PlanetTatooine) { name = "Tatooine" } 
} 

Problem I zostały próby rozwiązania jest zmniejszenie Gotowa na każdej klasy pochodnej Planet. Jak widać, wszystkie mają tę samą funkcję, co duplikaty func accept(visitor: PlanetVisitor) { visitor.visit(self) }.

Próbowałem wprowadzić domyślną implementację protokołu Planet i wdrożyć go na klasie bazowej, a Swift wydaje się, że nie pozwala na to z powodu kompilacji czasu przeciążenia.

Przykłady:

Domyślna implementacja na protokole: Klasa

extension Planet { 
    func accept(visitor: PlanetVisitor) { visitor.visit(self) } 
} 

Podstawa:

class PlanetBase: Planet { 
    func accept(visitor: PlanetVisitor) { visitor.visit(self) } 
} 

class PlanetAlderaan: PlanetBase {} 
class PlanetCoruscant: PlanetBase {} 
class PlanetTatooine: PlanetBase {} 

Czy ktoś zna sposób, że funkcja accept mogą być wykonane rodzajowy i stosowane automatycznie każda konkretna klasa wywodząca się z Planet? To nie jest kwestia krytyczna, ale jest świetną zagadką!

Odpowiedz

1

Krótka odpowiedź: niemożliwe, i jest to zgodne z projektem.

Wzór dla gości przeznaczony dla przypadku, gdy masz stabilną liczbę planet, ale nieznaną jeszcze liczbę odwiedzających. Więc planujesz przyszłe rozszerzenia w gościach, pisząc ten szablon raz. Dodanie większej liczby odwiedzających jest możliwe bez żadnych zmian na planetach.

W dużym projekcie możesz przejść do generowania kodu.


Nie poleca Twoja alternatywa jest bezpośrednie przełączenie planet, żaden kod boilerplate potrzebne:

func foo(planet: Planet) { 
    if planet is PlanetAlderaan { 
     name = "Alderaan" 
    } 
    else if planet is PlanetCoruscant { 
     name = "Coruscant" 
    } 
    else if planet is PlanetTatooine { 
     name = "Tatooine" 
    } 
} 

To jest podatne na błędy, ponieważ można łatwo zapomnieć planety. Wzór użytkownika zmusza do napisania kodu we wszystkich przypadkach, w przeciwnym razie nie zostanie skompilowany.

+0

Masz rację! Z jakiegoś powodu myślałem, że inne języki, takie jak C++, mogą zaimplementować metodę akceptacji w klasie bazowej, ale okazuje się, że się myliłem. Zobacz: https://stackoverflow.com/questions/17190873/c-visitor-pattern-why-should-every-derived-visited-implement-accept –

0

Reading @paiv odpowiedzieć mam pomysł, że można zmniejszyć boilerplate unikając zapominania case problem:

enum Planet { 
    case alderaan 
    case coruscant 
    case tatooine 

    func accept(visitor: PlanetVisitor) { 
     visitor.visit(planet: self) 
    } 
} 

protocol PlanetVisitor { 
    func visit(planet: Planet) 
} 

class NameVisitor: PlanetVisitor { 
    var name = "" 

    func visit(planet: Planet) { 
     switch planet { 
     case .alderaan: 
      name = "Alderaan" 
     case .coruscant: 
      name = "Coruscant" 
     case .tatooine: 
      name = "Tatooine" 
     } 
    } 
} 

Jeżeli nie użyje default w switch to gwarantuje, że kompilator nie pozwoli kod skompilować jeśli sprawa jest nie obsługiwane.

Ale myślę, że niektóre inne bloki mogą migrować wewnątrz typu Planet.

+1

Wzorzec Odwiedzający został częściowo zaprojektowany, aby umożliwić kodowanie stylu enum w obiektowym Języki.Biorąc pod uwagę, że Swift ma duże wsparcie enum, wzór Odwiedzających jest zbędny. Jeśli przyjrzysz się uważnie temu kodowi, wszystkie klasy "PlanetVisitor" mogą zostać zastąpione przez funkcję, która bezpośrednio pobiera wyliczenie "Planet". Nie widzę tu żadnej korzyści dla wzorca 'Visitor'. – hashemi

Powiązane problemy