2016-11-23 11 views
5

Я реализую шаблон посетителя в Swift 2.2 для проекта на работе.Как уменьшить шаблон с шаблоном посетителя в Swift?

Так что мне не нужно сводить мой исходный код и сэкономить некоторое время, я буду использовать 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" } 
} 

Проблема, которую я пытался решить, чтобы уменьшить шаблонного на каждый класс, производный от Planet. Как вы можете видеть, все они имеют одну и ту же функцию, дублированную func accept(visitor: PlanetVisitor) { visitor.visit(self) }.

Я попытался поместить реализацию по умолчанию на протокол Planet и реализовать его в базовом классе, и Swift, похоже, не разрешает его из-за компиляции временного разрешения перегрузки.

Примеры:

По умолчанию Реализация по протоколу:

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

Базовый класс:

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

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

Кто-нибудь знает способ, что функция accept может быть общим и автоматически применяется к каждый конкретный класс, который происходит от Planet? Это не критическая проблема, но это отличная головоломка!

ответ

1

Короткий ответ: невозможно, и это по дизайну.

Шаблон посетителя, предназначенный для случая, когда у вас есть стабильное количество планет, но неизвестно, но количество посетителей. Таким образом, вы планируете будущие расширения в посетителях, записывая этот шаблон сразу. Тогда добавление большего количества посетителей возможно без изменений планет.

В большом проекте вы можете использовать генерацию кода.


Не рекомендуется, ваша альтернативой является прямым переключением планет, не шаблонного кода не требуется:

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

Это подвержены ошибкам, так как вы можете легко забыть планеты. Шаблон посетителя заставляет вас писать код для всех случаев, иначе он не будет компилироваться.

+0

Вы правы! По какой-то причине я думал, что другие языки, такие как C++, могут реализовать метод accept в базовом классе, но оказывается, что я ошибался. См. Https://stackoverflow.com/questions/17190873/c-visitor-pattern-why-should-every-derived-visited-implement-accept –

0

Чтения @paiv ответа я получил идею, что вы можете уменьшить шаблонный, избегая при этом забывания проблемы случая:

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" 
     } 
    } 
} 

Если вы не будете использовать default в switch это гарантирует, что компилятор не воспрепятствует код компилируется, если какой-либо случай не обрабатывается.

Но я думаю, что некоторые другие шаблоны могут мигрировать внутри Planet типа.

+1

Шаблон посетителя был изобретен частично, чтобы кодирование стиля enum было объектно-ориентированным языки.Учитывая, что Swift имеет отличную поддержку перечисления, шаблон Visitor является избыточным. Если вы внимательно посмотрите на этот код, любые классы «PlanetVisitor» могут быть заменены на функцию, которая непосредственно переводит enum «Planet». Здесь я не вижу никакой пользы от шаблона «Посетитель». – hashemi