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

时间:2017-09-08 17:20:16

标签: swift swift-protocols protocol-oriented

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

场景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()的类继承协议,我会获得什么好处?

4 个答案:

答案 0 :(得分:3)

如果要将此foo功能添加到多个UIViewController(或您拥有的)子类,有几种方法:

  1. 带扩展名的协议方法:

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

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

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

    当您需要直接与Cocoa集成的共享功能时,这非常棒。例如,这可以在执行自定义视图控制器转换时使用,并且不希望在各种视图控制器中重复代码。这种方法可以是single responsibility principle的一种表现形式,可以帮助对抗视图控制器臃肿。

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

    1. 正如matt建议的那样,您还可以将foo实现为某些共享基类的扩展。

      当例程不仅对你的两个现有子类有意义,而且对所有子类都有意义时,这很有用。但如果这只是那两个特定子类的独特功能,那么这是不合适的。

    2. 您也可以将foo放入该基类的子类(例如FooViewController子类UIViewController),然后让您的前两个子类继承新FooViewController 1}} class。

      这解决了基类的简单扩展的不加区分的性质。

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

    3. 最重要的是,正确的方法取决于您尝试解决的具体问题。您的例子太过通用,我们无法提供具体的建议。

答案 1 :(得分:2)

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

假设你有三个函数的类控制器

Class Controller {

  func foo() { }

  func bar() { }

  func fooBar() { }
}

现在您需要另一个只需要foo()功能的对象,但是,通过子类控制器,您现在拥有所有3个功能。或者您需要复制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子类中的任何其他函数时,你将删除它。

在有限的情况下,它可能没有多大区别。但是,您永远不会知道将来需求可能会发生变化,并且最好在变更时为自己留出最大的灵活性。

答案 2 :(得分:1)

  

所以我创建了一个控制器类,两个UIViewController类都使用控制器类的实例

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

我建议的不是协议也不是类继承。在类本身上使用extension。您可以扩展UIViewController,并且presto,所有UIViewController子类都具有注入的功能。

答案 3 :(得分:0)

  

通过将foo()函数放在协议中然后让需要foo()的类继承协议,我可以获得什么好处?

如果您使用协议,您将能够存储通用SomeProtocol的实例,而无需关心其实际内容:

var myFooable: SomeProtocol

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

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

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