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

4

我一直在努力理解协议式编程,但是我不明白以下两种情况的区别...

情况1 我有两个类都是UIViewControllers。这两个类都需要使用某些共同的功能,因此我创建了一个协议和一个扩展,其中包含协议的默认实现,然后视图控制器只需要在类行中拥有该协议,就可以自动继承所需的功能,即...

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

class FirstViewController: UIViewController, SomeProtocol {
    ...

    func doSomething(){
        foo()
    }
}

class SecondViewController: UIViewController, SomeProtocol {
     ...

    func doSomethingElse(){
        foo()
    }
}

场景2 我有两个类是UIViewControllers。这两个类都需要使用一些共同的功能,因此我创建了一个控制器类,两个UIViewController类都使用控制器类的实例。即...

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()函数的类从该协议继承,我能获得什么优势呢?


所提出的问题似乎与面向协议的编程无关... - matt
4个回答

4

当您想要将foo功能添加到多个UIViewController(或其他类)子类时,有几种方法:

  1. 使用扩展的协议方法:

    问题在于,虽然这在仅使用Swift代码时非常有效,但在编写必须由Cocoa直接调用的代码时效果不佳。

    此方法的优点(如果可以)是您开始享受WWDC 2015中概述的面向协议的编程的优势。

  2. 组件方法(其中您拥有一些Controller实例,您的视图控制器可以出售):

    当您需要与Cocoa直接集成的共享功能时,这很好。例如,在执行自定义视图控制器转换并且不想在各种视图控制器中重复代码时,可以使用此方法。此方法可以是单一职责原则的体现,并有助于打击视图控制器膨胀。

为了完整起见,还有一些选项可用于实现重用:

  1. 正如Matt所建议的那样,您也可以将foo实现为一些共享基类的扩展。

    当这个例程不仅对您的两个现有子类有意义,而且对所有子类都有意义时,这非常有效。但是如果这个功能只属于这两个特定的子类,那么这种方法就不合适了。

  2. 您也可以将foo放入该基类的一个子类中(例如,FooViewController子类化UIViewController),然后让您之前的两个子类再子类化这个新的FooViewController类。

    这解决了简单扩展基类的无差别性。

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

底线是,正确的方法取决于您要解决的具体问题。您的示例过于通用,我们不能提供具体的建议。


2

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

假设您有一个带有三个功能的控制器类

Class Controller {

  func foo() { }

  func bar() { }

  func fooBar() { }
}

现在,您的情况是需要另一个只需要foo()功能的对象,然而,通过子类化Controller,您现在拥有所有三个函数。或者您需要复制func foo()代码以使两个类都具有该功能。

 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
}

为了减少这些限制并使你的代码更易读/清晰,你可以创建一个协议 Fooing 并将类型分配给 Fooing。
 protocol Fooing {
   func foo()
 }

 class SomeViewController: UIViewController {

   let controller: Fooing
 }

通过这个更改,你可以做到以下几点:
1- 在你的代码中表明你期望这个对象只能做一件事情,就是调用foo()函数。
2- 你不再局限于使用Controller类,而是可以使用符合Fooing标准的任何对象,这样提供了更多的灵活性。
3- 你避免了在Controller子类中调用其他函数时可能出现的问题。
在某些情况下,这种更改可能没有太大的区别。然而,未来的需求变化是无法预料的,最好留给自己最大的灵活性。

0
foo()函数放在协议中,然后让需要foo()的类继承该协议,我会得到什么优势?
如果使用协议,您将能够存储一个通用的SomeProtocol实例,而不必关心它实际上是什么:
var myFooable: SomeProtocol

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

另一方面,使用Controller的实例是一个坏主意。任何类都可以创建一个新的Controller对象并调用其方法。你在这里试图表达一个“能够”的属性,这就是“符合协议”的作用。如果您使用Controller的实例,那将表达一个“具有”的关系。

此外,为什么要使用带有默认实现的协议呢?如果您不会在符合类中有不同的协议方法实现,请考虑将该方法制作为全局函数或将其转换为“辅助方法类”中的静态方法。


0
所以我创建了一个控制器类,两个 UIViewController 类都使用控制器类的实例。
这个想法的问题是:如果说你想在 UITableViewController 上使用该类怎么办?你无法将控制器类转换为 UITableViewController 的超类,因此不可能实现。
我的建议既不是协议,也不是类继承,而是对类本身使用 extension。你可以扩展 UIViewController, 那么所有的 UIViewController 子类都将具有注入的功能。

但如果我不需要每个UIViewController都有这个功能,只需要其中几个呢? - MikeG
我不明白重点在哪里。UIViewControllers已经拥有了大量你不需要的功能。所有内置类都是如此。如果它们拥有更多功能,你又为何在乎呢?如果你不需要某个方法,你就不要去调用它。 - matt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接