Skip to main content

Basics of Object-Oriented Programing


Basics of Object-Oriented programming with Swift examples
Object-Oriented programming is a paradigm centered around objects that bundles data and behaviour together. Swift, as a modern language, encourages OOP.
There are 4 OOP principles: Encapsulation, Inheritance, Polymorphism and Abstraction.

1. Encapsulation

Encapsulation restricts direct access to an object's data and allows controlled modification through methods. By restricting some of the object's properties (using access control modifiers like private), property modification can occur only by using dedicated methods.

class Person {
    private var name: String

    init(name: String) {
        self.name = name
    }

    func getName() -> String {
        return name
    }

    func setName(newName: String) {
        name = newName
    }
}

let person = Person(name: "Alice")
print(person.getName())  // Alice
person.setName(newName: "Bob")
print(person.getName())  // Bob

In this example, property name is private and it's inaccessible from the outside. Only by using dedicated methods for getting the name and changing the name, can it be modified.

2. Inheritance

Inheritance allows one class to inherit properties and behaviors from another existing class (a superclass). This allows methods and properties from superclass to be inherited by the subclass, enabling for code reusability and clean modification/extension as needed.

class Animal {
	var name: String
    
    init(name: String) {
    	self.name = name
    }
    
    func makeSound() {
        print("Some generic animal sound")
    }
}

class Dog: Animal {
	var breed: String
    
    init(breed: String, name: String) {
    	self.breed = breed
        super.init(name: name)
    }
    
    override func makeSound() {
        print("Woof!")
    }
}

let myDog = Dog(breed: "Pomeranian", name: "Zaro")
myDog.makeSound()  // Woof!

Here, the Dog class inherits from Animal and overrides makeSound() to provide a specific implementation.

3. Polymorphism

Polymorphism enables a single interface to handle different types. This is useful when there are collection of objectes that share the common type, but behave differently when method is called.

let animals: [Animal] = [
	Animal(name: "Boby")
    Dog(breed: "Husky", name: "Thor")
]

for animal in animals {
	animal.makeSound()
}

In this loop, even though array contains Animal types, when makeSound() is called, if it's overriden by a subclass, that method implementation is executed. This flexibility is the point of Polymorphism.

4. Abstraction

Abstraction hides complex details and exposes only the relevant parts. Swift leverages protocols to achieve polymorphism.

protocol Vehicle {
    func startEngine()
}

class Car: Vehicle {
    func startEngine() {
        print("Starting car engine...")
    }
}

class Motorcycle: Vehicle {
    func startEngine() {
        print("Starting motorcycle engine...")
    }
}

let myCar: Vehicle = Car()
let myBike: Vehicle = Motorcycle()

myCar.startEngine()  // Starting car engine...
myBike.startEngine()  // Starting motorcycle engine...

Here, Vehicle is an abstract concept (defined as a protocol), and specific vehicle types (Car and Motorcycle) implement it differently. These principles form the foundation of object-oriented programming in Swift. Swift programming language also embraces value data types like structs and enums, which can offer advantages in safety and performance.
Key differences: Classes are reference types and do support inheritance, while structs are value types that are copied when modified and passed around. Use best of both worlds, a mix of object-oriented programming with protocol-oriented programming approaches offer clean and maintainable codebase

BONUS: Difference between Abstraction and Inheritance

  1. Relationship vs. Contract 
    1.   Inheritance is about hierarchy, while Abstraction "defines a contract", e.g. anything that can "make a sound" need to implement makeSound() method without overriding it. 
  2.  Code sharing vs. Flexibility 
    1.  Inheritance is about reusing code and adding specific functionality while Abstraction focuses on exposing capabilities. 
  3.  Implementation details 
    1.  By using Inheritance, subclasses often inherit both method signature and implementation details of the superclass, while with Abstraction via protocols, only method signature is defined. Each conforming type to the protocol decides it's implementation details.

Comments