Why Protocol Is Better Than Class in Swift

Why protocol is better than class in swift?

Lets take a downloading example.

You have a Base class FileDownloadModel, and have 3 subclasses AudioFileDownloadModel, VideoFileDownloadModel, and ImageDownloadModel.

You have a DownloadManager that takes a FileDownloadModel input and uses this model's urlToDownload property to download the file.

Later down the line you are told that there is one more model coming but it's of type UserDownloadModel which is a subclass of User, and not FileDownloadModel.

See now it becomes difficult to handle this scenario where you will have to change a lot of code to incorporate downloading methods.

How protocol oriented programming will help you here:

  1. Create a protocol named DownloadingFileProtocol and add
    methods and properties that you need for downloading a file. eg. urlToDownload,
    pathToSave, extension etc.
  2. Implement the same protocol in FileDownloadModel and
    UserDownloadModel. The benefit here is that you don't have to change a
    lot of code in UserDownloadModel. You will just implement the
    methods from the DownloadingFileProtocol.
  3. If a new entity comes down the line again, you will not
    change any code. Rather, you'll just implement the protocol methods.
  4. And now your DownloadManager can take a
    DownloadingFileProtocol as input instead of a specific model. As well, you can now make any model "downloadable" by having it adopt this protocol.

What is the Difference Between Classes vs Protocols

A class serves as a blueprint for creating one or more objects based on specific implementation of that class.
A good analogy is a form for cutting out butter-cookies. The form‘s attributes (shape, size, height) define the cookies that you can cut out with it. You have only one form (class) but you can create many cookies (instances of that class, ie. objects) with it. All cookies are based on that particular form.
Similarily all objects that are instances of that class are identical in their attributes.

Classes = data and methods (special functions), all sophistically bundled together.

Classes define, what their inner content (data) is + what kind of work (methods) they can do.
The content is based on variables that hold various number types, strings, constants, and other more sophisiticated content + methods which are chunks of code that (when executed) perform some computational operations with various data.

All methods defined in class have their
Definition - that defines the name of the method + what (if any) data the methods takes in for processing and what (if any) data the methods spits out for processing by someone else. All methods defined in class also have Implementation – the actual code that provides the processing – it is the innerworkings of methods.. inside there is code that processes the data and also that is able to ask other methods for subprocessing data. So the class is a very noble type in programming.

If you understand the above, you will understand what a protocol is.

A protocol is a set of one or more method declarations and that set has a name and represents a protocol. I say declarations, because the methods that together are defined by a particular protocol, do not have any implementation code defined.. The only thing that exist is their names declared.
Look above - in class, you have always defined not only what methods the class has, but also how that work will be done. But methods in protocol do not have any implementation.

Lets have a real life analogy again, it helps. If you come to my house to live here for a week, you will need to adhere to my TidyUp protocol. The TidyUp protocol defines three methods - wash the dishes every day, clean the room, and ventilate fresh air. These three methods, I define them..are something you will do. But I absolutely do not care, how the implementation should look like, I just nominaly define the methods. You will implement them, ie.you define how the details of that work (those methods) will look like. I just say, adhere to my protocol and implement it as you see fit.

Finale – You can declare some class. You can separately also declare a protocol. And you can then declare, that this class, in addition to its own methods, will adopt or adhere to that protocol, ie. the class wil implement the protocol’s methods.

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

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.

Inheritance in Class Vs Protocol

Code 1 and code 2 are fundamentally not the same.

What is the difference between Code 1 and Code 2 as both of them serves the same purpose?

No they don't. The first one defines a class hierarchy, the second defines a protocol (an API, if you like) and a type that conforms to it.

In code 2, classNew is inheriting from new protocol or just conforming to the protocol?

It's conforming to the protocol. There's no inheritance involved (if you are being pedantic).


Code 1 defines a base class and a class that inherits from it. The subclass overrides the base class for the function abc() and it behaves as you would expect given a class hierarchy i.e.

let x: New = ClassNew()
let y: ClassNew = ClassNew()

print(x.abc()) // prints "derived class"
print(y.abc()) // prints "derived class"

Both print statements call the derived class's abc()

In code 2 you define a protocol with no methods, and an extension to the protocol with an extension method. Note that this is not a "default method" there is nothing in the protocol to default. You then define a class that conforms to the protocol and adds a new method that happens to have the same name as the extension method. The distinction (from the pure class hierarchy) is important because the version of abc() called is determined statically at compile time

protocol New2{}

extension New2{
func abc(){
print("new protocol")
}
}

class ClassNew2: New2 {
func abc() {
print("derived protocol")
}
}

let y2: ClassNew2 = ClassNew2()
let x2: New2 = y2

print(x2.abc()) // prints "new protocol"
print(y2.abc()) // prints "derived protocol"

Even though x2 and y2 are the same object different versions of the function are called. This is because the compiler is not allowed to assume anything about x2 except what it can infer from the protocol. So it doesn't know that the object has anabc() of its own so it must call the extension function.

If you had defined the protocol like this:

protocol New3{
func abc()
}

extension New3{
func abc(){
print("new protocol")
}
}

class ClassNew3: New3 {
func abc() {
print("derived protocol")
}
}

let y3: ClassNew3 = ClassNew3()
let x3: New3 = y3

print(x3.abc()) // prints "derived protocol"
print(y3.abc()) // prints "derived protocol"

This time the compiler knows that the object should have a function abc() and will only use the extension function if it doesn't. Then the normal inheritance rules for classes apply.

Swift: Protocol vs. Struct vs. Class

These analogies are not "exactly" correct, but this is the gist of it as I understand it

  1. Yes, Protocols are effectively like interfaces

  2. Classes are classes, like in Java/Android, and pretty much any other language

  3. Structs are like classes, but they are passed by-value (copied) when passing them from one variable/function to another.
    If you're familiar with C# at all, it's implementation of structs is very similar.

Eg:

class Foo {
...
}

let x = Foo()
let z = x

At this point x and z both refer to the same Object in memory, there is only one Foo object

struct Bar {
...
}

let a = Bar()
let b = a

When assigning b, a is copied (think of basically copying the memory block). At this point, there are two independent Bar objects in memory and modifying one doesn't affect the other.

Why is this useful? Sometimes you don't want a shared reference, but mostly for performance reasons. Because structs don't have to all refer to the same object, they don't have to be allocated on the heap. They can instead often be allocated on the stack, which is much faster. Also arrays of structs can be implemented as one big contiguous block of memory which means it's much friendlier on the CPU cache if you want to iterate through it all.

Swift isn't garbage collected, but for garbage collected languages like C# this can mean the garbage collector doesn't have to deal with a lot of objects that it might otherwise have to. Even in swift the struct copying means it can avoid doing the Retain/Release behind the scenes neccessary for ARC, which can help a lot.

The primary use case for structs is when you have lots of immutable "simple data" like a Vector (set of 3 floating point values)

What is difference between '&' and ',' in swift about Protocol Composition

From the Swift documentation on protocols, I think & is mainly used when you are requiring a variable to adopt to multiple protocols, ex:

func wishHappyBirthday(to celebrator: Named & Aged) {
print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}

And you use , when you want to adopt a custom struct/class to multiple protocols:

struct SomeStructure: FirstProtocol, AnotherProtocol {
// structure definition goes here
}

Why Choose Struct Over Class?

According to the very popular WWDC 2015 talk Protocol Oriented Programming in Swift (video, transcript), Swift provides a number of features that make structs better than classes in many circumstances.

Structs are preferable if they are relatively small and copiable because copying is way safer than having multiple references to the same instance as happens with classes. This is especially important when passing around a variable to many classes and/or in a multithreaded environment. If you can always send a copy of your variable to other places, you never have to worry about that other place changing the value of your variable underneath you.

With Structs, there is much less need to worry about memory leaks or multiple threads racing to access/modify a single instance of a variable. (For the more technically minded, the exception to that is when capturing a struct inside a closure because then it is actually capturing a reference to the instance unless you explicitly mark it to be copied).

Classes can also become bloated because a class can only inherit from a single superclass. That encourages us to create huge superclasses that encompass many different abilities that are only loosely related. Using protocols, especially with protocol extensions where you can provide implementations to protocols, allows you to eliminate the need for classes to achieve this sort of behavior.

The talk lays out these scenarios where classes are preferred:

  • Copying or comparing instances doesn't make sense (e.g., Window)
  • Instance lifetime is tied to external effects (e.g., TemporaryFile)
  • Instances are just "sinks"--write-only conduits to external state (e.g.CGContext)

It implies that structs should be the default and classes should be a fallback.

On the other hand, The Swift Programming Language documentation is somewhat contradictory:

Structure instances are always passed by value, and class
instances are always passed by reference. This means that they are
suited to different kinds of tasks. As you consider the data
constructs and functionality that you need for a project, decide
whether each data construct should be defined as a class or as a
structure.

As a general guideline, consider creating a structure when one or more
of these conditions apply:

  • The structure’s primary purpose is to encapsulate a few relatively simple data values.
  • It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an
    instance of that structure.
  • Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced.
  • The structure does not need to inherit properties or behavior from another existing type.

Examples of good candidates for structures include:

  • The size of a geometric shape, perhaps encapsulating a width property and a height property, both of type Double.
  • A way to refer to ranges within a series, perhaps encapsulating a start property and a length property, both of type Int.
  • A point in a 3D coordinate system, perhaps encapsulating x, y and z properties, each of type Double.

In all other cases, define a class, and create instances of that class
to be managed and passed by reference. In practice, this means that
most custom data constructs should be classes, not structures.

Here it is claiming that we should default to using classes and use structures only in specific circumstances. Ultimately, you need to understand the real world implication of value types vs. reference types and then you can make an informed decision about when to use structs or classes. Also, keep in mind that these concepts are always evolving and The Swift Programming Language documentation was written before the Protocol Oriented Programming talk was given.

What is Protocol Oriented Programming in Swift? What added value does it bring?

Preface: POP and OOP are not mutually exclusive. They're design paradigms that are greatly related.

The primary aspect of POP over OOP is that is prefers composition over inheritance. There are several benefits to this.

In large inheritance hierarchies, the ancestor classes tend to contain most of the (generalized) functionality, with the leaf subclasses making only minimal contributions. The issue here is that the ancestor classes end up doing a lot of things. For example, a Car drives, stores cargo, seats passengers, plays music, etc. These are many functionalities that are each quite distinct, but they all get indivisibly lumped into the Car class. Descendants of Car, such as Ferrari, Toyota, BMW, etc. all make minimal modifications to this base class.

The consequence of this is that there is reduced code reuse. My BoomBox also plays music, but it's not a car. Inheriting the music-playing functionality from Car isn't possible.

What Swift encourages instead is that these large monolithic classes be broken down into a composition of smaller components. These components can then be more easily reused. Both Car and BoomBox can use MusicPlayer.

Swift offers multiple features to achieve this, but the most important by far are protocol extensions. They allow implementation of a protocol to exist separate of its implementing class, so that many classes may simply implement this protocol and instantly gain its functionality.



Related Topics



Leave a reply



Submit