• 20 hours
  • Medium

Free online content available in this course.

Videos available in this course

Certificate of achievement available at the end this course

Got it!

Last updated on 2/4/19

Override methods

Log in or subscribe for free to enjoy all this course has to offer!

At this point we’ve managed to figure out how to utilize a common function in a parent class and make it produce different results for each subclass. Let’s be honest, it looks clumsy :'(. With this approach, the base class must be aware of all subclasses. Which violates the principle of abstraction!

As usual, there’s a better way!

In this chapter we will learn the next principle of OOP, called Polymorphism.

Poly-morphism-what :waw:?

Never mind, Polymorphism in simple terms means overriding the base implementation via a new implementation, which is provided by a subclass.

Overriding the methods

Referring to our animal communication example, our base class may implement some general animal communication. And since we don't know which animal we are dealing with, it's unreasonable to predict how it would communicate. To avoid guessing and incorrect results, let's call it silent communication. Then, each of our subclasses will take responsibility for implementing their own version of this method. Which would be barking for Dogs and meowing for Cats!

This way, the base class Animal need not know anything about subclasses!

And when we decide to create a horse class, we’ll inherit the Animal class and we will simply implement  horse communication!

Yeah, whatever that is...
Whinny! 

Overriding syntax

To override a method we need to declare a new function within a subclass which will follow the exact same declaration syntax as the original function, plus the keyword  override . We need to place the keyword in the very beginning of this new declaration AND the new implementation:

class ParentClassName {
    func doSomething() {
        print("I'm a function in parent class")
    }
}

class ChildClassName: ParentClassName {
    override func doSomething() {
        print("I'm a function in child class")
    }
}

Let's verify the execution:

var parentInstance = ParentClassName()
var childInstance = ChildClassName()

parentInstance.doSomething() // I'm a function in parent class
childInstance.doSomething() // I'm a function in child class

Similarly for our animal set of classes:

class Animal {
    // properties
    var color = "brown"
    var weight = 20
    
    // methods
    /* common methods */
    
    func communicate() {
        print("Don't know what I can do, beacuse I don't know which animal I am")
    }
}

class Dog: Animal {
    override func communicate() {
        print("I can bark, I must be a dog!")
    }
}

class Cat: Animal {
    override func communicate() {
        print("I can meow, I must be a cat!")
    }
}

var myAnimal = Animal()
var myDog = Dog()
var myCat = Cat()

// observe results
myAnimal.communicate() // Don't know what I can do, beacuse I don't know which animal I am
myDog.communicate() // I can bark, I must be a dog!
myCat.communicate() // I can meow, I must be a cat!

Now, when we have a variable of a Dog type, and call the communicate function, the Dog’s implementation will be executed rather than the Animal’s communication. The original communication function implemented by the Animal class will be completely ignored! We can observe this behavior in the example above.

That’s cool and it's just what we needed in the first place! But how about a case when the base implementation is still valid and we just need to add on top of it?

That would be SUPER!

We don't alway have to completely replace the parent implementation, we can also build upon it. We can do this by explicitly calling the base function (parent implementation) in the subclass' function we are overriding:

class ParentClassName {
    func doSomething() {
        print("I'm a function in parent class")
    }
}

class ChildClassName: ParentClassName {
    override func doSomething() {
        doSomething() // inteding to call the base implementation
        print("I'm a function in child class")
    }
}

Wait! The example above doesn't work :-°. Since both of those function have identical declarations (that’s the requirement for overriding), we need to explicitly specify which version we are calling. By default it’s always a subclass version, so the  doSomething   ends up calling itself again and again and getting stuck in this loop.

To avoid the confusion in our doSomething function we can address the parent class using a special keyword  super  using the dot notation:

class ParentClassName {
    func doSomething() {
        print("I'm a function in parent class")
    }
}

class ChildClassName: ParentClassName {
    override func doSomething() {
        super.doSomething() // calling the base implementation
        print("I'm a function in child class")
    }
}

var childInstance = ChildClassName()
childInstance.doSomething()

This time we are calling our child function and get results from execution of both parent and child implementation:

I'm a function in parent class 
I'm a function in child class

This brings back a memory of a sibling keyword:  self  . Remember?  We used it to address properties of an instance itself.

So, let's review the two:

  • keyword  self  is used to address the elements that the object itself. If it’s a base class, it will refer to itself - the base, if it’s a subclass of another class - it will refer to itself again - in this case - that subclass.

  • keyword  super  is used to address a parent class - exactly one level up!

An ability to override the implementation in subclasses sounds helpful!

Improving further NeXTDestination

We've established that one of the most exciting parts of our journey will be...telling the story! Part of our story will be describing each entertainment experience we had. So, let's add a method for it to our parent class:

class Entertainment {
    /* ... */
    func tellStory() -> String {
        return "It was fun to experience \(name)"
    }
    /* ... */
}

Now, we can call this method on each of our subclasses! This is great, but it's boring. We don't highlight any particulars of each experience :colere:.

By now we know that we can create a unique implementation for each subclass!

Take some time to tell a story about each kind of entertainment experience by overriding the  tellStory()  method in each of the subclasses for the Entertainment class: Activity, ArtsGastronomy

Incorporate as much information from each object as possible. As a constraint, consider the following:

  • Include the super class implementation at least once.

  • Use at least one subclass specific property.

  • Use switch with enum at least once. 

  • Use conditional output at least once (i.e. an if/else statement; present different pieces of information depending on the chosen condition).

// No scrolling yet!


























class Entertainment {
    //properties
    static let generalOptions = Entertainment.generateGeneralOptions()
    var name: String
    var cost: Double
    var location: String
    
    static func generateGeneralOptions() -> [Entertainment] {
        var options = [Entertainment]()
        
        // activities
        let activity = Activity(name: "Horseback riding", cost: 60)
        activity.difficulty = .intermediate
        activity.equipmentRequired = "A helmet"
        activity.weatherConditions = "No heavy rain"
        options.append(activity)
        
        // arts
        let arts = Arts(name: "MoMA. Items: Is Fashion Modern?", cost: 12)
        arts.genre = .modern
        arts.teaser = "Items: Is Fashion Modern? explores the present, past—and sometimes the future—of 111 items of clothing and accessories that have had a strong impact on the world in the 20th and 21st centuries—and continue to hold currency today. "
        arts.ageLimit = 0
        options.append(arts)
        
        // gastronomy
        let gastronomy = Gastronomy(name: "Horseback riding", cost: 60)
        gastronomy.cuisine = "Italian"
        gastronomy.dressCode = .casual
        gastronomy.minNumberOfPeople = 4
        options.append(gastronomy)
        
        return options
    }
    
    //initializer
    init(name: String, cost: Double) {
        self.name = name
        self.cost = cost
        location = ""
    }
    
    func tellStory() -> String {
        print("It was fun to experience \(name)")
    }
}

enum Difficulty: String {
    case hard = "Hard"
    case intermediate = "Intermediate"
    case easy = "Easy"
}

enum Genre: String {
    case classical = "Classical"
    case modern = "Modern"
    case historical = "Historical"
}

enum DressCode: String {
    case streetwear = "Streewear"
    case casual = "Casual"
    case formal = "Formal"
}

class Activity: Entertainment {
    var weatherConditions = ""
    var difficulty: Difficulty = .intermediate
    
    override func tellStory() -> String{
        // intro - print using the parent implementation
        super.tellStory()
        
        var story = " "
        
        // mention about weather requirements
        story += "Oh, the weather requirement: \(weatherConditions). "
        
        // finish off with the financial impact:)
        if cost <= 30 {
            story += "All that for ONLY $\(cost). "
        }
        else if cost >= 100 {
            story += "It was costly - $\(cost) - but definitely worth it. "
        }
        else {
            story += "It was afforable at a price of $\(cost). "
        }
        
        // share the full story
        return story
    }
}

class Arts: Entertainment {
    var genre: Genre = .modern
    var teaser = ""
    
    override func tellStory() -> String{
        // intro
        var story = "\(name) was an interesting experience! "
        
        // talk about genre
        story += " It was \(genre.rawValue) - "
        switch genre {
        case .modern:
            story += " my cup of tea! "
        default:
            story += " I'm not a big fan, but it was educational! "
        }
        
        // share teaser
        story += teaser
        
        // finish off with the financial impact:)
        story += "It cost me $\(cost). "
        
        // share the full story
        return story
    }
}

class Gastronomy: Entertainment {
    var dressCode: DressCode = .casual
    var cuisine = ""
    
    override func tellStory() -> String{
        // intro
        var story = "\(name) was outstanding! "
        
        // tell about cuisine
        story += "\(cuisine) cuisine was delicious. "
        
        // share the dress code info
        story += "There was a \(dressCode.rawValue) - "
        switch dressCode {
        case .streetwear:
            story += " no need change before going! "
        case .casual:
            story += " nothing fancy, very simple! "
        case .formal:
            story += " - a great opportunity to dress up! "
        }
        
        // finish off with the financial impact:)
        story += "All that for $\(cost). "
        
        // share the full story
        return story
    }
}

Let's review what was done here:

  • We implemented an individual story telling method by overriding the parent method  tellStory()  .

  • Each implementation highlights general and subclass specific information.

  • Each implementation demonstrates different story telling approaches (some information in presented in a factual way, some in more fluid fashion).

Now let's test it out using our general options:

for entertainment in Entertainment.generalOptions {
    print(entertainment.tellStory())
}

Your results may be different from the example above. This is more than ok! This is a story telling part and you can present it in the way you believe is most interesting to the listener.

Congrats on your progress!

Let's Recap!

  • Keyword  super   provides access to properties and methods of a parent class within a child class.

  • Keyword  override   allows us to modify a method implementation defined in a parent class by either completing it or redefining it entirely in a child class.

Example of certificate of achievement
Example of certificate of achievement