Свифт-протокол-ориентированные расширения

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

Получите следующий фрагмент кода, который я пробежал по игровым площадкам

protocol ProtocolA {
    func doSomethingA()
}

protocol ProtocolB {
    func doSomethingB()
}

protocol ProtocolC {
    func doSomethingC()
}

extension ProtocolA {
    func doSomethingA() {
        print("Extension - doSomethingA")
    }
}

extension ProtocolA where Self: ProtocolB {
    func doSomethingA() {
        print("Extension - doSomethingA Self: ProtocolB")
    }
}

extension ProtocolA where Self: ProtocolC {
    func doSomethingA() {
        print("Extension - doSomethingA Self: ProtocolC")
    }
}

extension ProtocolA where Self: ProtocolB, Self: ProtocolC {
    func doSomethingA() {
        print("Extension - doSomethingA Self: ProtocolB, ProtocolC")
    }
}

extension ProtocolB {
    func doSomethingB() {
        print("Extension - doSomethingB")
    }
}

extension ProtocolC {
    func doSomethingC() {
        print("Extension - doSomethingC")
    }
}

class Implementation: ProtocolA, ProtocolB, ProtocolC {
}

let obj = Implementation()

obj.doSomethingA()

То, что я получаю напечатано:

Extension - doSomethingA Self: ProtocolB, ProtocolC

Есть ли в любом случае, что я могу гарантировать, что все расширения для запуска.

В идеале я хотел бы получить следующий вывод.

Extension - doSomethingA
Extension - doSomethingA Self: ProtocolB
Extension - doSomethingA Self: ProtocolC
Extension - doSomethingA Self: ProtocolB, ProtocolC

Я понимаю, что Swift выберет самое сильное совпадение с точки зрения его типов, на самом деле, если я не предоставлю реализацию, где ProtocolA совпадает с ProtocolB и ProtocolC, я получу ошибку времени компиляции. Есть ли в любом случае, что я могу обойти это?

Благодарю.

1 ответ

Решение

Задача расширений состоит в том, чтобы обеспечить реализацию по умолчанию для методов протокола.

В вашем примере компилятор разрешит вызывать doSomethingA() метод расширения, который лучше всего подходит для вашего конкретного объекта, но это все.

Как вы можете видеть в вашей точке вызова:

obj.doSomethingA()

Вы выполняете один вызов метода. Это будет решено и отправлено одному вызову метода, а не четырем.

Это похоже на то, что когда вы переопределяете метод через подклассы и вызываете метод базового класса, вызывается только производный метод, а не базовый класс. В этом случае, если вы хотите, вы можете позвонить super.method() из подкласса.

В вашем случае, если вы хотите, чтобы вызывалось несколько методов, вы можете дать им отдельные имена и реализации через расширения, а затем просто создать класс, который реализует все протоколы следующим образом:

class A: ProtocolA, ProtocolB, ProtocolC {

   func all() {
      doSomethingA()
      doSomethingB()
      doSomethingC()
   }
}

Преимущество расширений здесь в том, что ваш class A не должен предоставлять реализации для трех методов.

Другие вопросы по тегам