采用协议+扩展 VS 使用实例的区别 class

Difference between adopting protocol+extension VS using instance of a class

我一直在努力掌握面向协议的编程,但我不明白以下两种情况之间的区别...

场景一 我有两个 class 是 UIViewControllers。这两个 classes 都需要使用一些通用功能,所以我创建了一个协议和一个带有协议默认实现的扩展,然后视图控制器只需要在 class 行中包含协议,它们将自动继承所需的功能。即...

protocol SomeProtocol {
    func foo()
}
extension SomeProtocol {
    func foo(){
        //execute
    }
}

class FirstViewController: UIViewController, SomeProtocol {
    ...

    func doSomething(){
        foo()
    }
}

class SecondViewController: UIViewController, SomeProtocol {
     ...

    func doSomethingElse(){
        foo()
    }
}

场景二 我有两个 class 是 UIViewControllers。这两个 classes 都需要使用一些通用功能,因此我创建了一个控制器 class 并且两个 UIViewController classes 都使用了控制器 class 的一个实例。即...

class FirstViewController: UIViewController {
    ...
    let controller = Controller()

    func doSomething(){
        controller.foo()
    }
}

class SecondViewController: UIViewController {
    ...
    let controller = Controller()

    func doSomethingElse(){
        controller.foo()
    }
}


class Controller {
    func foo(){
        //execute...
    }
}`

那么有什么区别呢?在任何需要使用 foo() 函数的地方,我都可以获取 Controller() 的一个实例。通过将 foo() 函数放在协议中然后让需要 foo() 的 classes 从协议继承,我有什么优势?

What advantage do I get by putting the foo() function in a protocol and then having the classes that need foo() inherit from the protocol?

如果您使用协议,您将能够存储通用实例 SomeProtocol 而无需关心它实际是什么:

var myFooable: SomeProtocol

显然,这并不总是有用。如果你不需要存储这样一个通用的实例,使用协议似乎有点过分。

另一方面,使用 Controller 的实例是个坏主意。任何 class 都可以创建一个新的 Controller 对象并在其上调用方法。你在这里试图做的是表达一个 "is able to" 属性,这就是 "conforming to a protocol" 所做的。如果您使用 Controller 的实例,则表示 "has a" 关系。

此外,您为什么还要使用默认实现的协议?如果您不打算在符合 classes 的情况下对协议方法进行不同的实现,请考虑将该方法设为全局函数或将其转换为 "helper methods class".

中的静态方法。

so I create a controller class and both UIViewController classes use an instance of the controller class

这个想法的问题是:你会怎么做,比如说,一个 UITableViewController?你不能,因为你不能把你的控制器 class 变成 UITableViewController 的超级class。

我建议的既不是协议也不是 class 继承。在 class 本身上使用 extension。您可以扩展 UIViewController,很快,所有 UIViewController 子classes 都具有注入的功能。

当您只需要 class 的特定功能时,面向协议的编程有助于减少子class 更大 class 的冗余。

假设您有 class 具有三个功能的控制器

Class Controller {

  func foo() { }

  func bar() { }

  func fooBar() { }
}

现在您需要另一个只需要 foo() 功能的对象,但是,使用 subclassing Controller,您现在拥有所有 3 个功能。或者您需要复制 func foo() 代码以在两个 class 中都具有功能。

 class SomeViewController: UIViewController {
  /// You only need `func foo()` in this class, but now you have to use an object that carries all the extra functionality you don't need. 
  let controller: ControllerSubclass
}

为了限制这些约束并使您的代码更readable/clear,您创建一个协议 Fooing 并将类型分配给 Fooing。

 protocol Fooing {
   func foo()
 }

 class SomeViewController: UIViewController {

   let controller: Fooing
 }

通过进行此更改,您:

1 - 在您的代码中指明您希望此对象做它能够做的一件事,调用 foo() 函数。

2 - 您不限于控制器 class,您可以使用任何符合 Fooing 的对象,提供更大的灵活性。

3 - 您消除了 Controller subclass 中的任何其他函数在不应该被调用时被调用的机会。

在有限的情况下,它可能不会有太大的不同。但是,您永远不知道将来需求何时会发生变化,最好让自己在需求发生变化时保持最大的灵活性。

当您想将此 foo 功能添加到多个 UIViewController(或您拥有的)subclasses:

时,有几种方法
  1. 带有扩展的协议方法:

    问题是,虽然这对于仅 Swift 的代码非常有效,但当您编写必须由 Cocoa 直接调用的代码时,它就不能很好地工作了。

    这种方法的优点(如果可以)是您开始享受 WWDC 2015 中概述的优势 Protocol-Oriented Programming in Swift

  2. 组件方法(你有一些 Controller 你的视图控制器可以提供的实例):

    当您需要将直接与 Cocoa 集成的共享功能时,这非常有用。例如,这可以在进行自定义视图控制器转换时使用,并且不想在各种视图控制器中重复代码。这种方法可以是 single responsibility principle 的一种体现,可以帮助对抗视图控制器的膨胀。

为了完整起见,还有几个实现重用的选项:

  1. 正如 matt 所建议的,您还可以将 foo 实现为某些共享基础 class.

    的扩展

    当例程不仅对您现有的两个子class而且对所有子class都有意义时,这非常有效。但是,如果这只是那两个特定子classes.

  2. 所独有的功能,那么这是不合适的
  3. 您还可以将 foo 放入该基数 class 的子 class 中(例如 FooViewController subclasses UIViewController), 然后让你之前的两个subclass然后subclass那个新的FooViewController class.

    这解决了基数简单扩展的不加选择的性质 class。

    问题在于这不如前两种方法灵活(例如,没有多重继承,一组 Fooable 方法和另一组 Barable 方法)。

最重要的是,正确的方法取决于您要解决的具体问题。您的示例过于笼统,我们无法提供具体建议。