4

Swift question, Say for example you have a protocol Bark:

protocol MakeSound {
   func bark()
}

A super class Dog, that implements bark and also swim:

class Dog: MakeSound {
}

Then different types of dog that extend that:

class Poodle: Dog {
}

class GermanShephard: Dog {
}

class SheepDog: Dog {
}

But poodles yap, they don’t bark… all dogs bark, they just do it differently…How do I give them specific Bark behaviour?

Extend a protocol?...

extension MakeSound {
    func bark()
    func yapper()
}

But then Poodles and German Shephards have both behaviours (a yapping German Shephard?!)

If I make 2 extensions and check the type class using where Self = ?

extension MakeSound where Self: GermanShephard {
    func bark() {
        print("Bark")
    }
}

extension MakeSound where Self: Poodle{
    func yapper() {
        print("yap yap")
    }
}

But I can only check for one class type, or dog type. SheepDogs also bark but I can't check here...

I know In Java you can extend an interface with several different implementations. How can you do this using protocols in Swift using protocols to solve this problem?

Ben Smith
  • 521
  • 3
  • 15

5 Answers5

3

If I understand your question correctly, maybe this well help.

you can give bark() a default implementation by extending the protocol. Then on other classes that conform to the protocol you can change the implementation of the bark function:

protocol Bark {
   func bark()
}

//default implementation
extension Bark {
      func bark() { print("Bark") }
}

class Dog: Bark {}

//By calling bark func in Poodle, you change the default implementation.
class Poodle: Dog {
   func bark() { print("Yap") }
}

class GermanShephard: Dog {
     func bark() { print("Woof") }
}

let dog = Dog()
let poodle = Poodle()
let germanShephard = GermanShephard()

dog.bark()
//Bark
poodle.bark()
//Yap
germanShephard.bark()
//Woof

You can also not do any default implementation and just add your own for each situation

Edit after comment:

This is one of the main reason protocols are useful. They remove the tight coupling involved with subclassing. This is a base example as the entire topic is a lot of info but rather than subclassing Dog you can make a protocol DogRepresentable and assign all the default proprties and functions that all dogs implement the same. Then you can extend DogRepresentable where self: UIViewController and implement the default functionality:

protocol Barkable {
    func bark()
}

protocol DogRepresentable: Barkable {
//properties and functions all dogs will have with same implementation
}

extension DogRepresentable where Self: UIViewController {
//default implementation for functions all dogs will use
}

By assigning Barkable to DogRepresentable, you know any class that conforms to DogRepresentable will also have to conform to Barkable.

Now when you assign DogRepresentable to a class, it will get all the default implementation a base class would get, and you are forced to call the bark() function to conform to the protocol properly:

class Dog: DogRepresentable {
   func bark() { print("Bark") }
} 

class Poodle: DogRepresentable {
   func bark() { print("Yap") }
}

 class GermanShephard: DogRepresentable {
    //Won't conform because it doesn't have bark()
}

This way you get all the default implementation like the base class but won't have the issue of forgetting to override the superclass function.

Edit 2 based on second comment:

In that case your best bet is to leave DogRepresentable without conforming to Barkable and then create protocols for different types: so if you have dogs that yap you could do a protocol Yappable which has a bark function and default implementation. Then you can have another protocol Barkable with its own bark function and its own default implementation. Then have the class conform to whichever protocol they should conform to. Poodle conforms to Yappable, Dog conforms to Barkable.

By creating these separate protocols you can keep the functionality for each situation in one spot and only use whichever one you need, keeping your code cleaner, more readable, and DRY.

JustinM
  • 2,202
  • 2
  • 14
  • 24
  • Thanks, I just edited the question above to be a bit more clear. I think your solution could work...But what if Poodle forgot to override and then it starts barking? Can i protect against this? – Ben Smith Dec 15 '16 at 11:15
  • 1
    I mean say for example if I have lots of dogs that yap. I don't want to repeat the code by overriding the bark function everywhere. I want another protocol function in MakeSound for yapping, but only allow certain types of dogs to use that. – Ben Smith Dec 15 '16 at 11:25
  • Your indentation is... artistic? – Alexander Dec 15 '16 at 21:32
  • lol, the second time you got me today. Let me see what I can do. – JustinM Dec 15 '16 at 22:11
2

This is a perfect use case for protocol inheritance.

protocol DogSoundMaker {
    func makeSound()
}

protocol Barker: DogSoundMaker {}
protocol Yapper: DogSoundMaker {}
protocol Woofer: DogSoundMaker {}

extension Barker {
    func makeSound() { print("Bark") }
}

extension Yapper {
    func makeSound() { print("Yap yap, I am a glorified rodent") }
}

extension Woofer {
    func makeSound() { print("Woof") }
}

struct Poodle: Yapper {}
struct GermanShephard: Barker {}
struct SheepDog: Woofer {}

Poodle().makeSound()
GermanShephard().makeSound()
SheepDog().makeSound()
Alexander
  • 59,041
  • 12
  • 98
  • 151
0

This is the answer, makeSound protocol is implemented by 2 different barking sounds protocols dogYap and dogBark. Which in turn extend and implement the different sounds for barking and yapping. Then the classes of various dog types (which extends dog) can implement dogYap or dogBark, depending on what kind of sound that dog makes.

protocol MakeSound {
    func makeSound()
    var canBark: Bool { get }
    var canYap: Bool { get }
}

protocol dogBark: MakeSound {
    func makeSound()
}

protocol dogYap: MakeSound {
    func makeSound()
}

extension dogYap {
    func makeSound() {
        print("Yap")
    }
}

extension dogBark {
    func makeSound() {
        print("bark")
    }
}

extension MakeSound {
    func makeSound() {}
    var canBark: Bool { return self is dogBark }
    var canYap: Bool { return self is dogYap }

}

class Dog {
    var age: Int?
    var colour: UIColor?

}

class Poodle: Dog, dogYap {
}

class GermanShephard: Dog, dogBark  {
}

class SheepDog: Dog, dogBark {
}

//German shephard and Belgian bark in the same way
let germanShep = GermanShephard()
germanShep.makeSound()
germanShep.canBark
germanShep.canYap


let sheepDog = SheepDog()
sheepDog.makeSound()
sheepDog.canBark
sheepDog.canYap

let poodle = Poodle()
poodle.makeSound()
poodle.canBark
poodle.canYap
Ben Smith
  • 521
  • 3
  • 15
  • 1
    I would discourage a design like this. Base types should not have information on their derived types. Such an approach makes the base type not extensible. – Alexander Dec 15 '16 at 21:27
  • You mean the base type Dog having age and colour is information on their base types? Basically from your answer above (thanks by the way) and here I can see that you have not used inheritance at all. Where should age and colour of each dog be stored then? – Ben Smith Dec 16 '16 at 07:43
  • No, I'm talking about how the `MakeSound` protocol is aware of, and depends on, its derived protocols, `dogBark`, and `dogYap`. This design doesn't allow me to extend `MakeSound` to `dogWoof`, for example. – Alexander Dec 16 '16 at 07:46
  • It is aware of and depends on derived protocols because I am using an extension on it, and defining canBark/canYap to be true if the derived protocols (dogBark, dogYap) are implemented? So I will put those properties for canBark and canYap in my Dog baseclass? – Ben Smith Dec 16 '16 at 10:44
  • Is that a question...? – Alexander Dec 16 '16 at 15:29
  • Yeah, sorry! My question was basically where should the properites go that I put in MakeSound, so its not aware of its derived protocols? – Ben Smith Dec 17 '16 at 09:08
  • Those variables aren't necessary. Youre essentially using those 2 bools as part of a runtime psudo-type-system. There's no need for that. See my answer – Alexander Dec 17 '16 at 14:07
0

I believe this is the correct way of implementing this kind of behaviours:

import UIKit

protocol DogSoundMaker {}
protocol Barker: DogSoundMaker {}
protocol Yapper: DogSoundMaker {}

extension DogSoundMaker{
    var canBark: Bool { return self is Barker }
}
extension Barker {
    func makeSound() {
        print("Bark")
    }
}
extension Yapper {
    func makeSound() {
        print("Yap")
    }
}

class GermanShepherd: Barker {

}

class Poodle: Yapper{

}

class Chiwawa: Yapper {

}

var germanShep = GermanShepherd()
var poodleDog = Poodle()
poodleDog.makeSound()
poodleDog.canBark
germanShep.canBark
germanShep.makeSound()
Ben Smith
  • 521
  • 3
  • 15
0

As long as the parent class is not marked final, it's functions can be overridden.
Now, when a class conforms to a protocol, you are basically fulfilling the protocol's requirements by adding the variables/functions.

In your case, since Dog class has not been marked as final, you can simply override the bark function.
Nothing special required, it's just a function after all

Example:

protocol Barkable {
    func bark()
}

class Dog: Barkable {
    func bark() {
        print("Dog barks")
    }
}

class Poodle: Dog {
    override func bark() {
        print("Poodle yaps")
    }
}

class GermanShephard: Dog {
    override func bark() {
        print("GermanShephard barks")
    }
}

class SheepDog: Dog {
    override func bark() {
        print("SheepDog barks")
    }
}

Dog().bark()
Poodle().bark()
GermanShephard().bark()
SheepDog().bark()
staticVoidMan
  • 19,275
  • 6
  • 69
  • 98