Object-Oriented Programming: Your Path to Better Code

Object-Oriented Programming: Your Path to Better Code

Started out with Simula-67

Simula 67 is a general-purpose programming language that was developed at the Norwegian Computing center. It was built and designed to be a framework for Object-oriented programming languages. And considered as the first object-oriented programming language. It's features includes classes, inheritance, garbage collection, Simulation capability.

Programming Paradigms

To describe a paradigm in terms of OOP it’s a method or process to solve some problem or do some task.

Programming paradigms are approaches we use to solve problems using programming languages, there are various number of programming languages that are known but in order to use these languages to solve problems all of them need to follow some strategy when they’re implemented and this strategy or methodology is paradigms.

What’s OOP

Well OOP stands for Object-Oriented-Programming, is a method, style that a programmer employs by representing behaviors and patterns. It’s a great way to reuse code and make it more compact, ultimately leading to fewer errors, less time wasted, better readability and reusable code.

Some Basic OOP concepts include:

  1. Class

  2. Objects

  3. Data Abstraction

  4. Encapsulation

  5. Inheritance

  6. Polymorphism

Used the Object Oriented Programming with Python and it’s concepts

Python is a wonderful and fantastic programming language that allows you to use both functional and object-oriented programming paradigms.

Classes

Python like every other object-oriented language, allows you to define classes to create objects. In built Python classes are the most common data types in Python, such as lists, dictonaries, and so on.

Implementing the Class for Students

A class is a collection of instance variables and related methods that define a particular object type, it’s a user-defined data type. It acts as an object’s blueprint or template.

Attributes are the names that are given to the variables that make up a class.

I’ll define a class named Student1 for the student application software.

class Student:
    def __init__(self,firstname,lastname,age,sex):
        self.firstname = firstname
        self.lastname =lastname
        self.age = age
        self.sex = sex
print(f"Hello {Student1.firstname} and your age is {Student2.age}")

The init is a special method, that’s also known as a Constructor, is used initialize the Student1, with several attributes such as the First name, Last name, age and sex.

In python classes are written in lower class but user-defined classes are named using Camel casing or Snake case with the first letter capitalized.

Creating the Instances

This class can be instantiated to any number of objects. Three students are instantiated in the following code:

#Create instances
book1, book2 and book3 are distinct objects of the class Book. The term self in the attributes refers to the corresponding instances (objects).
Student1 = Student("Lawani Elyon","John",23,"M")
Student2 = Student("Gojo","Satorou",21,"M")
Student3 = Student("Hinata","Hyuga",20,"F")

Student1, Student2, Student3 are distinct objects of the class Student. The term self in the attribute refers to the corresponding instances

print(f"Hello {Student1.firstname} and your age is {Student2.age}")

Output:

Abstraction

Data abstraction refers to providing only the essential information about the data to the outside world, while hiding the background details or implementation.

Inheritance

Inheritance is the capability of a class to derive the properties and characteristics from another class. It is the most significant characteristic of OOP.

The Parent class

 #Parent class
class Bird:
    def __init__(self):
        print("Bird is Ready")

    def whoisThis(self):
        print("Bird")

    def swim(self):
        print("Swim Faster")

The Parent class Bird has the 3 methods which are the __init__() this will initialize the object another method whoisThis() which prints out a text and the method swim which prints another text.

The Child class

#Child class
class penguin:
    def __init__(self):
        #call the super function
        super().__init__()
        print("The Penguin is Ready")
    def whoisThis(self):
        print("Hi im Penguin")
    def run(self):
        print("And i run faster")

The penguin class inherits from the Bird class but not declared explicitly with Bird, the __init__() in penguin calls the __init__() method from the parent class using super().__init__() this will ensure the class initialization logic, the whoisThis method is overriden in the penguin class to print out the text, the run method is unique to the penguin class and print out the text

Creating an Object named peggy

peggy = penguin()
print(peggy.whoisThis())
print(peggy.run())

When we created the object peggy the child class penguin is initialized the super().__init__() in penguin calls the parent class Bird’s __init__()method printing the first text and after that the text for the penguin class is printed.

Encapsulation

Encapsulation is defined as the process of preventing clients from accessing certain properties, which can be only accessed through specific methods

class Book:
    def __init__(self, title, quantity, author, price):
        self.title = title
        self.quantity = quantity
        self.author = author
        self.price = price
        self. __discount = 0.10

    def __repr__(self):
        return f"Book({self.title}, {self.quantity}, {self.author}, {self.price})"

book1 = Book('Book 1', 12, 'Author 1', 120)

print(book1.title)
print(book1.quantity)
print(book1.author)
print(book1.price)
print(book1.__discount)

The output shows all the attributes printed out except the private attribute __discount. Using getters and setters methods we can access the private attributes.

class Book:
    def __init__(self, title, quantity, author, price):
        self.title = title
        self.quantity = quantity
        self.author = author
        self.price = price
        self. __discount = None

    # Setter
    def set_discount(self, discount):
        self.__discount = discount

    # Getter    
    def get_price(self):
        if self.__discount:
            return self.price * (1 - self.__discount)  
        return self.price  

    def __repr__(self):
        return f"Book({self.title}, Quantity: {self.quantity}, Author: {self.author}, Price: {self.get_price()})"

i hive created two objects, one for the purchase of a single book and another for the purchase books in bulk quantity.

While purchasing books in bulk quantitates, we get a discount of 10%

single_book = Book("Two States", 1, "Chetan Bhagat", 200)
bulk_book = Book("Two States", 100, "Chetan Bhagat", 200)
bulk_book.set_discount(0.10)

print(single_book.get_price())
print(bulk_book.get_price())  
print(single_book)  
print(bulk_book)

Output

200
180.0
Book(Two States, Quantity: 1, Author: Chetan Bhagat, Price: 200)
Book(Two States, Quantity: 100, Author: Chetan Bhagat, Price: 180.0)

Method Overloading

Method overloading refers to the use of many methods with the same name that take various numbers of arguments within a single class

The following example makes it clear

class MathOperations:
    def add(self, a, b, c=0): # Default value for c allows flexibility
        return a + b + c

math_op = MathOperations()

print(math_op.add(1, 2))        #  Uses a=1, b=2, default c=0 → Output: 3
print(math_op.add(1, 2, 3))     #  Uses a=1, b=2, c=3 → Output: 6

Python does not support traditional method overloading like Java or C++, where multiple methods can have the same name but different parameter lists.

Method Overriding

class Animal:
    def speak(self):
        return "Some sound"

class Dog(Animal):
    def speak(self):
        return "Bark"

class Cat(Animal):
    def speak(self):
        return "Meow"

# Example usage
animal = Animal()
dog = Dog()
cat = Cat()

print(animal.speak())  # Output: Some sound
print(dog.speak())     # Output: Bark
print(cat.speak())     # Output: Meow

When a method with the same name and arguments is used in both a derived class and a base or super class, we say that the derived class method "overrides" the method provided in the base class.

class ParentClass:
    def call_me(self):
        print("Calling method from ParentClass...")
        print("I am parent class")

class ChildClass(ParentClass):
    def call_me(self):
        print("Calling method from ChildClass...")
        print("I am child class")
        print("Now calling ParentClass method using super()...")
        super().call_me()

# Creating an instance of ParentClass
print("Creating an instance of ParentClass:")
pobj = ParentClass()
pobj.call_me()
print("-" * 30)  # Separator for clarity

# Creating an instance of ChildClass
print("Creating an instance of ChildClass:")
cobj = ChildClass()
cobj.call_me()

Output

Creating an instance of ParentClass:
Calling method from ParentClass...
I am parent class
------------------------------
Creating an instance of ChildClass:
Calling method from ChildClass...
I am child class
Now calling ParentClass method using super()...
Calling method from ParentClass...
I am parent class

The advantages and disadvantages of OOP

Its Advantages

  1. Modularity: With OOP we can break code down into smaller, more manageable chunks

  2. Scalable: Using abstraction and encapsulation our applications become more scalable

  3. Security: With Encapsulation our data is bundled and makes our code secure and free from unintended data corruption

  4. Code maintainability: When we use Objects and classes in OOP we make it easier to maintain and modify existing code in our application

  5. Reusability: Software developers can build different software products without starting from scratch each time

It’s Disadvantages

  1. Complexity: When using OOP some practices are generally not ideal for simpler projects

  2. Performance issues: When we use some OOP methods our applications may fall victim to performance issues

  3. Overriding issues: When using inheritance in our applications it can lead to overriding issues

  4. Memory Overhead: OOP can lead to memory overhead

Conclusion

Object-oriented programming languages have revolutionized the way developers approach software design. These languages enhance modularity, reusability, and scalability, making them invaluable in the current world that is technology-driven