Introduction to Object Oriented Programming (OOP)

Introduction

Object Oriented Programming (OOP) is a type of Programming paradigm that centers around the concept of "objects". You can think of an object as anything. For example, if you look around you, what do you see?. Maybe a chair, a pen or a person, those are objects. An object is a basic unit of a program. It can have an identity, state, and/or behavior. An object represents real-world entities and it is an instance of a class. A class is a blueprint that defines the structure, behavior, and characteristics of an object, serving as a template for its instances.
OOP focuses on improving code readability, reusability and maintainability. The are many programing languages today that support OOP, such as Python, Java, Kotlin and so on. In this article, I will be using the Python programming language but the same concepts can be applied to other languages as well.

Below shows a code snippet for a basic class and it's corresponding object

# Define a class called person
class Person:
    pass

# Create an object of the person class
person = Person()

Principles of OOP
There are four principles of OOP, which form the basis of OOP design. They are Encapsulation, Inheritance, Polymorphism and Abstraction.

Encapsulation: This principle involves bundling data and behaviors together into a single unit (the class) and hiding the data from other external entities. This principle ensures that the data is protected from unauthorized access or modification. It does this using access modifiers. Access modifiers help to control the visibility and accessibility of the attributes and methods of an object from outside the class. Examples of access modifiers are public, protected and private.

For example, In Python, there is no strict enforcement of access modifiers like in some other programming languages. However, we can still achieve encapsulation by convention using underscores to indicate the visibility of attributes and methods.

Public: attributes and methods with no underscore prefix can be accessed from anywhere, including outside the class.

Protected: attributes and methods with a single underscore prefix can accessed from within the class and its subclasses.

Private: attributes and methods with a double underscore prefix can only be accessed from within the class.

Below shows a code snippet demonstrating encapsulation. The variable height is completely hidden and cannot be accessed outside the Person class

class Person:
  def __init__(self, name, age, height):
    self.name = name        # public attribute
    self._age = age         # protected attribute
    self.__height = height # private attribute

  def get_details(self):
    return f"{self.name} is {self._age} years old and is {self.__height}ft tall"

person = Person("Dominik", 21, 5)
print(person.name)
print(person._age)
# print(person.__height) -> We can't access this
print(person.get_details())

Inheritance: This principle allows new classes to be derived from existing classes, thereby inheriting their attributes and behaviors. A class can inherit from one or more parent classes, and the child class will have the attributes or behaviors of its parent class. A child class can also provide its specific implementation to the methods of the parent class This helps to reduce code duplication, making it easier to maintain and reuse code.

For example, Let's create a Student class that inherits from the Person class. We will give the student class an additional attribute “school”. Due to inheritance, we are able access the name and age variable and also call the get_details method even though it wasn’t declared in the Student class.

class Student(Person):
  def __init__(self, name, age, height, school):
    super().__init__(name, age, height)
    self.school = school

student = Student("Magret", 29, 6, "Federal University of Technology, Minna")
print(student.name)   # Output: Magret
print(student._age)   # Output: 29
print(student.get_details())

Polymorphism: This principle allows objects to exist in different types. For example, a person can be a student, an engineer, a doctor or a lawyer. An engineer can even be a father, a CEO and so on. In OOP, this means that you can access objects of different types through the same interface. Each type can provide its own custom implementation of this interface or simply rely on the default implementation.

To implement this in python, I will create an additional class (Engineer) that inherits from Person class and provides its own implementation of the get_details method. The object student is both a Student and a Person while the object engineer is both an Engineer and a Person. We can call the get_details method for both objects.

class Engineer(Person):
  def __init__(self, name, age, height, skills):
    super().__init__(name, age, height)
    self.skills = skills

  def get_details(self):
    return f"{super().get_details()}. {self.name} is also an Engineer with skills in {self.skills}"


student = Student("Magret", 29, 6, "Federal University of Technology, Minna")
engineer = Engineer("David", 30, 6.5, ["Java", "C++", "C#"])
print(student.get_details())
print(engineer.get_details())

Abstraction: This principle involves hiding the details of the implementation of an object and providing only essential data/information to the outside world. Imagine you want to turn on your PC, you don't need to know the inner workings of a PC or what happens behind the scene to turn on your PC, you only need to know which button to press to turn on your PC. This is the idea of abstraction in OOP and it helps to reduce complexity and make the code easier to read.

To implement this in python, let’s add an order_food method to our Student class. It takes a few steps to order food, but for a student using this program, we don’t want to bother them with the steps involved in getting this done. The student only get’s a response of whether the order was successful or not.

class Student(Person):
  def __init__(self, name, age, height, school):
    super().__init__(name, age, height)
    self.school = school

  def order_food(food_name: str):
    """
      Check if the food name is part of the MENU
      if true:
        Inform the student that the order is in progress
        prepare the food
        package the food
        deliver the food to the student's dome or hostel
      else:
        Inform the student that the food is currently not available
    """
    pass

student = Student("Magret", 29, 6, "Federal University of Technology, Minna")
student.order_food()

Benefits of OOP

  • OOP provides abstraction and polymorphism, which makes the code more flexible as well as easier to read and understand for other developers.

  • OOP allows us to break down complex software into smaller modules, making it easier to maintain. Also, changes made to a section of the code won't affect other parts of the software, reducing the risk of bugs and errors.

  • OOP provides Inheritance which enables us to reuse code by creating classes that can be used across different projects and applications. This means that we can avoid duplicating code which saves time and effort.

In conclusion, OOP is a programming paradigm that ensures modularity, code reuse, and abstraction. It provides a flexible and adaptable structure for building complex software systems.

Thank you for reading!

0
Subscribe to my newsletter

Read articles from Frank C. Ebeledike directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Frank C. Ebeledike
Frank C. Ebeledike

I am a mobile app developer | Java ° Kotlin ° Flutter | AI/ML Enthusiast| Python