Object Oriented Programming (OOP) Part 2

2024-06-30

Object Oriented Programming Thumbnail Image

When designing an application, there are four pillars that form the foundational principles of creating clean, object-oriented code. These four pillars are inheritance, polymorphism, encapsulation, and abstraction. These pillars work together to build organized, maintainable, and reusable code.

As explained in the previous edition, the goal of OOP is to achieve the following:

• Modularity: OOP breaks down a program into smaller objects, making the codebase easier to develop and maintain.

• Reusability: Code can be reused through the concept of inheritance.

• Maintainability: Encapsulation protects data integrity, simplifying the process of modifying code.

• Scalability: OOP facilitates easier development as new features and functionalities can be added through new or extended objects.

Inheritance

Inheritance is a mechanism in OOP that allows a new class (child) to inherit properties and behaviors from its parent class. This reduces code duplication and avoids redundancy by not repeating code.

Inheritance Image

Benefits of Inheritance:

• Elimination of code repetition: Instead of writing the same code repeatedly, shared functionality can be placed in a base class and inherited by subclasses.

• Modularity: Inheritance allows for a modular structure where classes are organized in a hierarchy, with each class handling a specific portion of functionality, making it easier to manage and maintain.

• Extensibility: New functionality can be added by creating new subclasses without modifying existing code.

• Code Maintenance: Changes to the base class are automatically reflected in all derived classes, ensuring consistent updates and reducing the risk of bugs.

• Representation of relationships: Inheritance creates a hierarchical structure from parent to child classes, allowing the system to be organized logically and intuitively.

    class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"

class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow!"

Types of Inheritance:

• Single Inheritance: A class inherits properties and behaviors from one base class.

• Multilevel Inheritance: A class is derived from another class, which is itself derived from another class, forming a chain of inheritance.

• Hierarchical Inheritance: Multiple classes inherit from a single base class.

• Multiple Inheritance: A class inherits from more than one base class.

Polymorphism

Polymorphism comes from the Greek words ‘poly’ meaning many and ‘morphe’ meaning forms. Etymologically, polymorphism means the ability to have different or multiple forms.

In OOP, polymorphism allows different classes to share the same method names, making them interchangeable in code.

Polymorphism Image

Benefits:

• Flexibility: Polymorphism enables a single method to operate on different objects, but it can be used with a unified implementation.

• Maintainability: Changes to a method’s implementation in the base class are automatically reflected in derived classes, simplifying code maintenance.

• Extensibility: New subclasses can be added without altering existing code. A new subclass can override methods without affecting code that uses the method with the same name.

• Decoupling: It reduces dependency between code and different objects that share the same interface.

    class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        raise NotImplementedError("Subclass must implement abstract method")

class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"

class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow!"

animals = [Dog("Rex"), Cat("Whiskers")]

for animal in animals:
    print(animal.speak())

Encapsulation

Encapsulation is the process of bundling data and methods within a single unit (class), restricting access to certain components of an instantiated object. Encapsulation is useful for maintaining object integrity and allowing changes to internal implementations without affecting the rest of the code that uses the object.

Encapsulation Image

Benefits:

• Data protection: By creating private fields and providing public getter and setter methods, encapsulation safeguards the data.

• Improved maintainability: It simplifies the maintenance process by localizing changes.

• Increased flexibility: Encapsulation allows internal code implementation to change without affecting the external interface, making code refactoring and optimization easier.

class Person:
    def __init__(self, name, age):
        self.__name = name  # private attribute
        self.__age = age    # private attribute

    def get_name(self):
        return self.__name

    def get_age(self):
        return self.__age

    def set_age(self, age):
        if age > 0:
            self.__age = age
        else:
            raise ValueError("Age must be positive")

person = Person("Alice", 30)
print(person.get_name())
print(person.get_age())
person.set_age(31)
print(person.get_age())

Abstraction

Abstraction is the process of hiding complex implementation details and only exposing the essential features of an object. This approach reduces code complexity and improves development efficiency.

There are two types of abstraction in Python:

1. Data abstraction: Focuses on the required data and how to interact with it, while the actual data entities are hidden through a data structure.

2. Process abstraction: Focuses on what a process does, but hides the implementation details of that process.

Abstraction Image

Benefits:

• Reduces complexity: By hiding complex implementation details and only showing essential features, abstraction simplifies code.

• Improves maintainability: Abstraction encapsulates implementation details, allowing changes in one class without affecting the rest of the codebase.

• Enhances reusability: It provides a generic template that can be reused in different parts of an application.

• Improves security: By exposing only the necessary methods and properties, abstraction reduces the risk of unintended access or modification of sensitive data.

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

rectangle = Rectangle(5, 10)
print("Area:", rectangle.area())
print("Perimeter:", rectangle.perimeter())

The beauty of programming is that there's always something new to learn. The learning process is never-ending, and that's what keeps it exciting. This is my ongoing journey into the world of programming. I'm constantly learning and growing, and I'm excited to share my experiences with you as I progress.

const developerName = "Ano Jumisa"