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

1.4k Views Asked by At

I have been trying to grasp protocol-oriented-programming but i don't understand the difference between the 2 following scenarios...

Scenario 1 I have two classes that are UIViewControllers. Both of these classes need to use some common functionality so I create a protocol and an extension with a default implementation of the protocol and then the view controllers only need have the protocol in the class line and they will automatically inherit the needed functionality. ie...

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

class FirstViewController: UIViewController, SomeProtocol {
    ...

    func doSomething(){
        foo()
    }
}

class SecondViewController: UIViewController, SomeProtocol {
     ...

    func doSomethingElse(){
        foo()
    }
}

Scenario 2 I have two classes that are UIViewControllers. Both of these classes need to use some common functionality so I create a controller class and both UIViewController classes use an instance of the controller class. ie...

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...
    }
}`

So what is the difference? Anywhere that I need to use the foo() function I could just grab an instance of Controller(). 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?

4

There are 4 best solutions below

0
On BEST ANSWER

There are a couple of approaches when you want to add this foo functionality to multiple UIViewController (or what have you) subclasses:

  1. The protocol approach with extensions:

    The problem is that while this works great with Swift-only code, it doesn't work so well when you're writing code that must be called by Cocoa directly.

    The merit of this approach (when you can) is that you start to enjoy the advantages outlined in WWDC 2015 Protocol-Oriented Programming in Swift.

  2. The component approach (where you have some Controller instance that your view controllers can vend):

    This is great when you need shared functionality that will be integrating directly with Cocoa. For example, this can be used when doing custom view controller transitions and don't want to repeat code in the various view controllers. This approach can be a manifestation of the single responsibility principle and can help fight view controller bloat.

For the sake of completeness, there are a few more options to achieve reuse:

  1. As matt suggested, you can also implement foo as an extension to some shared base class.

    This works great when the routine makes sense for not just your two existing subclasses, but all subclasses. But this is not appropriate if this is functionality unique to just those two particular subclasses.

  2. You can also put foo into a subclass of that base class (e.g. FooViewController subclasses UIViewController), and then have your two previous subclasses then subclass that new FooViewController class.

    This addresses the indiscriminate nature of a simple extension of the base class.

    The problem is that this is not as flexible as the first two approaches (e.g. no multiple inheritance, one set of Fooable methods and another set of Barable methods).

Bottom line, the right approach depends up the specific problem you're trying to solve. Your example is too generic for us to offer specific counsel.

2
On

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

The problem with that idea is: how would you do it for, say, a UITableViewController? You can't, because you can't turn your controller class into a superclass of UITableViewController.

What I would suggest is neither a protocol nor class inheritance. Use an extension on the class itself. You can extend UIViewController, and presto, all UIViewController subclasses have the injected functionality.

0
On

Protocol Oriented programming helps in reducing the redundancy of subclassing a larger class when you only need a specific functionality from that class.

Let's say you have class controller with three functions

Class Controller {

  func foo() { }

  func bar() { }

  func fooBar() { }
}

Now you have a situation where you need another object that only needs the functionality of foo(), however, with subclassing Controller, you now have have all 3 functions. Or you need to copy func foo() code to have the functionality in both classes.

 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
}

To limit these constraints and make your code more readable/clear, you create a protocol Fooing and assign the type to Fooing.

 protocol Fooing {
   func foo()
 }

 class SomeViewController: UIViewController {

   let controller: Fooing
 }

By making this change you:

1 - Indicate in your code you expect this object to do the one thing it's able to do, call foo() function.

2 - Your not limited to the Controller class, you can use any object that conforms to Fooing, providing much more flexibility.

3 - You remove the chance of any other functions in a Controller subclass being called when they aren't supposed to.

In limited situations, it may not make much of a difference. However, you never know in the future when requirements might change and it's always best to leave yourself the most flexibility for when they do change.

0
On

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?

If you use protocols, you will be able to store an instance of a generic SomeProtocol without caring about what it actually is:

var myFooable: SomeProtocol

Obviously, this is not always useful. If you don't need to store such a generic instance, using protocols seems a bit excessive.

On the other hand, using instances of Controller is a bad idea. Any class can create a new Controller object and call methods on it. What you are trying to do here is to express a "is able to" property, which is what "conforming to a protocol" does. If you use an instance of Controller, that would express a "has a" relationship.

In addition, why are you using protocols with default implementations anyway? If you are not going to have different implementations of a protocol method in conforming classes, consider making that method a global function or turning it into a static method in a "helper methods class".