Подтвердить что ты не робот

Расширение протокола vs расширение класса в Swift

Предположим, что существует протокол Draggable, который обычно будет соответствовать объекту UIView

protocol Draggable {
  drag()
}

Мы можем либо реализовать drag() в расширении протокола как option 1

// option 1
extension Draggable where Self: UIView {
  func drag() {
    // implementation
  }
}
extension UIView: Draggable {} // added after @Rich Tolley answer

Или мы можем реализовать drag() в расширении UIView как option 2

// option 2
extension UIView: Draggable {
  func drag() {
    // implementation
  }
}

Вот мой вопрос:

  • Есть ли разница между этими двумя подходами (вариант 1 и вариант 2)?
  • Если да, какая разница и как выбрать, когда мы проектируем наш проект или библиотеку?

И идея была бы полезна.

4b9b3361

Ответ 1

Да, есть разница: (EDIT: или, по крайней мере, в исходной версии этого q, который не добавил extension UIView : Draggable {} в конец опции 1).

  • Вариант 1 создает реализацию по умолчанию для экземпляров UIView, которые соответствуют Draggable. Вам по-прежнему нужно отметить UIView, который вы хотите соответствовать Draggable как таковой в объявлении: class MyView : Draggable. Все, что соответствует Draggable, но не является подклассом UIView, должно будет предоставить свою собственную реализацию.

  • Вариант 2 расширяет все UIView, чтобы они соответствовали Draggable. Ничто другое не может быть Draggable, если для этих классов не записаны отдельные расширения, или они соответствуют протоколу вручную. В объявлении класса нет необходимости добавлять Draggable.

Расширение протокола обычно является лучшим вариантом. В этом случае это, очевидно, верно, так как не все UIView могут быть Draggable. Кроме того, переход по маршруту расширения протокола означает, что вы можете создать объект Draggable, который не является подклассом UIView, если это необходимо (по общему признанию, маловероятно, так как большинство элементов управления Cocoa являются подклассами UIView - хотя и не все - UIBarButtonItem не странно)

Если вы выполните опцию 2, во многих случаях вы добавите ненужные методы в UIView, что является нарушением хорошего объектно-ориентированного дизайна - в частности, принципа разделения сегментов (клиенты не должны вынуждены полагаться на методы они не используют ), который является "I" в SOLID принципах

Ответ 2

Расширение протокола должно использоваться, если вы хотите реализовать функциональные возможности более чем для одного класса. В этом случае вы должны использовать extension UIView: Draggable, поскольку реализация относится к классу UIView.

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

protocol Location {
    var location: CGPoint { get set }
}

и вы хотите, чтобы каждый класс, который реализует местоположение, соответствует Draggable, тогда можно использовать расширение протокола:

extension Draggable where Self: Location {
    func drag() {
    }
}

Для получения дополнительной информации вы должны взглянуть на Протокольное программирование в Swift с WWDC 2015 года.