协议扩展与Swift中的类扩展

use*_*127 16 protocols ios swift

假设有一个协议Draggable,通常会被一个UIView对象 所遵循

protocol Draggable {
  drag()
}
Run Code Online (Sandbox Code Playgroud)

我们可以drag()在协议扩展中实现option 1

// option 1
extension Draggable where Self: UIView {
  func drag() {
    // implementation
  }
}
extension UIView: Draggable {} // added after @Rich Tolley's answer
Run Code Online (Sandbox Code Playgroud)

或者我们可以drag()UIView扩展中实现option 2

// option 2
extension UIView: Draggable {
  func drag() {
    // implementation
  }
}
Run Code Online (Sandbox Code Playgroud)

这是我的问题:

  • 这两种方法(选项1和选项2)之间是否存在差异?
  • 如果是,那么我们设计项目或图书馆时有什么区别以及如何选择?

想法会有所帮助.

Ric*_*ley 9

是的,存在差异:(编辑:或者至少在此q的原始版本中,没有添加extension UIView : Draggable {}到选项1的末尾).

  • 选项1为UIView符合的实例创建默认实现Draggable.您仍然需要在声明中标记UIView您希望遵守Draggable的内容:class MyView : Draggable.任何符合Draggable但不是UIView子类的东西都需要提供自己的实现.

  • 选项2扩展所有 UIView s以使它们符合Draggable.Draggable除非为这些类编写单独的扩展,否则其他任何内容都不可能,或者它们是手动符合协议的.没有必要Draggable在类声明中添加.

协议扩展通常是更好的选择.在这种情况下,这显然是正确的,因为不是所有UIView的都可以Draggable.此外,沿着协议扩展路由向下意味着你​​可以创建一个Draggable不是UIView子类的对象,如果有必要的话(不可否认,因为大多数Cocoa控件都是UIView子类 - 尽管不是全部 - UIBarButtonItem不是,奇怪的是)

如果你遵循选项2,你将UIView在很多情况下添加不必要的方法,这违反了良好的面向对象设计 - 特别是接口隔离原则(客户端不应该被迫依赖他们不使用的方法) - 这是SOLID原则中的'I'


Pal*_*lle 3

当您想要为多个类实现功能时,应该使用协议扩展。在这种情况下,您应该使用 ,extension UIView: Draggable因为该实现特定于 UIView 类。

假设您有一个提供位置的协议:

protocol Location {
    var location: CGPoint { get set }
}
Run Code Online (Sandbox Code Playgroud)

并且您希望每个实现 Location 的类都符合 Draggable,那么可以使用协议扩展:

extension Draggable where Self: Location {
    func drag() {
    }
}
Run Code Online (Sandbox Code Playgroud)

如需进一步参考,您应该查看2015 年 WWDC 上的《Swift 中的面向协议编程》