The Pillars of OOP and How They Work in Python: Part 3 Composition

winstonmhango23

6 min read

Views: 150

Welcome to the third part of our series on Object-Oriented Programming (OOP) in Python. In this part, we will delve into more advanced OOP concepts, including composition, aggregation, association, and the Method Resolution Order (MRO).

Advanced OOP Concepts

1. Composition

Composition involves constructing complex objects from simpler ones. It provides more flexibility than inheritance by allowing you to combine objects in various ways.

Theory and Basics

Composition is a fundamental concept in object-oriented programming (OOP) where a class is composed of one or more objects of other classes. This allows for building complex types by combining objects, giving more flexibility compared to inheritance, where classes are derived from other classes.

Key Concepts

  1. Reusability: By composing objects, we can reuse existing classes without modifying them.
  2. Flexibility: Composition allows you to change the behavior of a class at runtime by changing the composed objects.
  3. Encapsulation: It helps in encapsulating the functionality by delegating responsibilities to the composed objects.

Composition vs. Inheritance

  • Inheritance: Inheritance represents an "is-a" relationship. For example, a BankAccount is a type of FinancialAccount.
  • Composition: Composition represents a "has-a" relationship. For example, a BankAccount has a Customer.

Python Implementation

In Python, composition is achieved by including instances of other classes as attributes of a class.

Bank Account Example

Let's illustrate composition with a BankAccount and a Customer.

  1. Define the Customer Class
class Customer:
    def __init__(self, name, customer_id):
        self.name = name
        self.customer_id = customer_id

    def __str__(self):
        return f"Customer Name: {self.name}, Customer ID: {self.customer_id}"

Define the BankAccount Class

class BankAccount:
    def __init__(self, account_number, customer):
        if not isinstance(customer, Customer):
            raise ValueError("customer must be an instance of the Customer class")
        self.account_number = account_number
        self.customer = customer
        self.balance = 0.0

    def deposit(self, amount):
        if amount <= 0:
            print("Deposit amount must be positive")
            return
        self.balance += amount
        print(f"Deposited {amount:.2f}, new balance is {self.balance:.2f}")

    def withdraw(self, amount):
        if amount <= 0:
            print("Withdrawal amount must be positive")
            return
        if amount > self.balance:
            print("Insufficient funds")
            return
        self.balance -= amount
        print(f"Withdrew {amount:.2f}, new balance is {self.balance:.2f}")

    def get_balance(self):
        return self.balance

    def __str__(self):
        return f"Account Number: {self.account_number}, Balance: {self.balance:.2f}"

Using the Composite Classes

# Create a Customer instance
customer = Customer("John Doe", "CUST123")

# Create a BankAccount instance associated with the customer
account = BankAccount("001", customer)

# Perform operations on the bank account
account.deposit(500)
account.withdraw(200)
account.withdraw(400)  # This will trigger an "Insufficient funds" message

# Display account details
print(account)
print(account.customer)

Detailed Explanation

Step-by-Step Breakdown

  1. Defining the Customer Class:
    • The Customer class represents a bank customer with attributes name and customer_id.
    • The __str__ method provides a string representation of the customer, which is useful for displaying customer details.
  1. Defining the BankAccount Class:
    • The BankAccount class represents a bank account associated with a Customer.
    • The constructor initializes the account number, associates it with a Customer object, and sets the initial balance to zero. It also ensures that the customer parameter is an instance of the Customer class.
    • The deposit method increases the account balance by the specified amount, with validation to ensure the amount is positive.
    • The withdraw method decreases the account balance by the specified amount if sufficient funds are available, with validation to ensure the amount is positive and that there are enough funds.
    • The get_balance method returns the current balance.
    • The __str__ method provides a string representation of the bank account, displaying the account number and balance.
  1. Using the Composite Classes:
    • An instance of Customer is created with a name and a customer ID.
    • An instance of BankAccount is created with an account number and the previously created Customer object.
    • The deposit method is used to add funds to the account, and the withdraw method is used to withdraw funds, demonstrating how the balance changes.
    • The account details and customer details are printed using the __str__ methods of both classes.

Advantages of Composition

  1. Modularity: Each component can be developed and tested independently.
  2. Maintainability: Changes in one component class do not affect others.
  3. Flexibility: Components can be easily replaced or extended without modifying the composite class.

Conclusion

Composition is a powerful technique in OOP that promotes reusability, flexibility, and maintainability by building complex objects from simpler ones. In the bank account example, composition allows a BankAccount to include a Customer object, encapsulating the relationship between them. This approach adheres to good OOP principles, promoting modularity and making the code easier to maintain and extend.

Recent Related Posts

Related Posts

Creating and uploading a Python package to the PyPi: Part 3 Creating The package and uploading to pypi.org

In this part of the series, we will walk you through the final steps of creating a Python package and uploading it to PyPi. We will use the example package bank_creator that we discussed in Part 2. The full code for this package can be found on GitHub.

Read More

Creating and uploading a Python package to the PyPi: Part 2 Creating Git repository and uploading to github

Read More

Creating and uploading a Python package to the PyPi: Part 1 Basics

Read More

© 2024 .