Back

Interface and Abstraction in Python

🔹 Why Use an Interface?

  • 🔄 Scalability: You can add new payment methods (like CryptoPayment) without modifying existing code.
  • 🔧 Flexibility: The PaymentProcessor class works with any payment method that follows the IPaymentMethod interface.
  • ✅ Code Reusability: The same logic applies to multiple payment types without code duplication.
  • 📏 Enforcing Contract: All payment methods must implement process_payment and refund_payment, ensuring consistency.

🛠 Example: Using Interface in Python

from abc import ABC, abstractmethod

# Interface: Defines a common contract for all payment methods
class IPaymentMethod(ABC):
    @abstractmethod
    def process_payment(self, amount: float) -> bool:
        pass

    @abstractmethod
    def refund_payment(self, amount: float) -> bool:
        pass

# Concrete Class 1: Implements UPI Payment
class UPIPayment(IPaymentMethod):
    def __init__(self, upi_id: str):
        self.upi_id = upi_id

    def process_payment(self, amount: float) -> bool:
        print(f"Processing UPI payment of ₹{amount} via {self.upi_id}")
        return True

    def refund_payment(self, amount: float) -> bool:
        print(f"Refunding ₹{amount} to {self.upi_id}")
        return True

# Concrete Class 2: Implements Credit Card Payment
class CreditCardPayment(IPaymentMethod):
    def __init__(self, card_number: str, card_holder: str):
        self.card_number = card_number
        self.card_holder = card_holder

    def process_payment(self, amount: float) -> bool:
        print(f"Charging ₹{amount} to credit card {self.card_number} (Holder: {self.card_holder})")
        return True

    def refund_payment(self, amount: float) -> bool:
        print(f"Refunding ₹{amount} to credit card {self.card_number}")
        return True

# Concrete Class 3: Implements PayPal Payment
class PayPalPayment(IPaymentMethod):
    def __init__(self, email: str):
        self.email = email

    def process_payment(self, amount: float) -> bool:
        print(f"Processing PayPal payment of ₹{amount} for {self.email}")
        return True

    def refund_payment(self, amount: float) -> bool:
        print(f"Refunding ₹{amount} to PayPal account {self.email}")
        return True

# Payment Processor: Uses the Interface to Accept Any Payment Type
class PaymentProcessor:
    def __init__(self, payment_method: IPaymentMethod):
        self.payment_method = payment_method

    def make_payment(self, amount: float):
        if self.payment_method.process_payment(amount):
            print("Payment successful!")
        else:
            print("Payment failed!")

    def make_refund(self, amount: float):
        if self.payment_method.refund_payment(amount):
            print("Refund successful!")
        else:
            print("Refund failed!")

# -----------------
# ✅ Demonstrating the Usage
# -----------------

upi_payment = UPIPayment("user@upi")
card_payment = CreditCardPayment("1234-5678-9876-5432", "John Doe")
paypal_payment = PayPalPayment("[email protected]")

processor = PaymentProcessor(upi_payment)
processor.make_payment(1000.0)
processor.make_refund(500.0)

processor = PaymentProcessor(card_payment)
processor.make_payment(2500.0)
processor.make_refund(1000.0)

processor = PaymentProcessor(paypal_payment)
processor.make_payment(1500.0)
processor.make_refund(750.0)

🎯 Difference Between Interface and Abstraction

Feature Interface Abstraction
Definition A contract that enforces method implementation in child classes. A way to hide implementation details and show only relevant functionality.
Usage Used when multiple classes should follow a common structure. Used when we want to partially implement some methods but hide details.
Implementation No implementation of methods, only method signatures. Can have both abstract methods and concrete methods.
Flexibility Allows multiple different implementations to follow the same structure. Helps in simplifying complex systems by exposing only essential details.
Example IPaymentMethod defines a structure but doesn't implement methods. A base class Shape might have area() as an abstract method but implement display().

When to Use What?

  • Use Interface when you want to enforce a structure for different implementations (e.g., different payment methods).
  • Use Abstraction when you want to provide a base implementation but leave some methods to be defined by subclasses (e.g., a Shape class where area() is abstract but display() is implemented).

🚀 Final Thought:

Interfaces define "what should be done" but not "how", whereas Abstraction defines "what should be done" and partially "how" it should be done. Use them wisely to achieve a scalable and maintainable codebase!