Interfaces
Define contracts with interfaces for flexible, maintainable code.
Interfaces define contracts that classes must follow. They specify what methods a class must implement without specifying how they should be implemented. This allows for polymorphism and loose coupling.
What are Interfaces?
Section titled “What are Interfaces?”An interface is a contract that specifies what methods a class must implement. It defines the “what” but not the “how”.
Why Use Interfaces?
Section titled “Why Use Interfaces?”- Polymorphism - Different classes can be used interchangeably
- Loose Coupling - Depend on abstractions, not concrete classes
- Flexibility - Easy to swap implementations
- Testability - Easy to create mock implementations
- Multiple Inheritance - Classes can implement multiple interfaces
Python Interfaces
Section titled “Python Interfaces”Python doesn’t have explicit interfaces like Java, but uses:
- Abstract Base Classes (ABC) - Similar to interfaces
- Protocols - Structural subtyping (duck typing)
- ABC with
@abstractmethod- Enforces method implementation
from abc import ABC, abstractmethod
class PaymentProcessor(ABC): """Interface-like abstract class"""
@abstractmethod def process_payment(self, amount: float) -> bool: """Process a payment - must be implemented""" pass
@abstractmethod def refund(self, transaction_id: str) -> bool: """Process a refund - must be implemented""" pass
def validate_amount(self, amount: float) -> bool: """Concrete method - shared by all implementations""" return amount > 0
class CreditCardProcessor(PaymentProcessor): """Implements PaymentProcessor interface"""
def __init__(self, api_key: str): self.api_key = api_key
def process_payment(self, amount: float) -> bool: """Implement required method""" if not self.validate_amount(amount): return False print(f"Processing ${amount} via credit card") return True
def refund(self, transaction_id: str) -> bool: """Implement required method""" print(f"Refunding transaction {transaction_id} via credit card") return True
class PayPalProcessor(PaymentProcessor): """Implements PaymentProcessor interface"""
def process_payment(self, amount: float) -> bool: """Implement required method""" if not self.validate_amount(amount): return False print(f"Processing ${amount} via PayPal") return True
def refund(self, transaction_id: str) -> bool: """Implement required method""" print(f"Refunding transaction {transaction_id} via PayPal") return True
# Usage - polymorphismdef checkout(processor: PaymentProcessor, amount: float): """Works with any PaymentProcessor implementation""" return processor.process_payment(amount)
credit_card = CreditCardProcessor("api_key_123")paypal = PayPalProcessor()
checkout(credit_card, 100.0) # Workscheckout(paypal, 100.0) # WorksProcessing $100.0 via credit cardProcessing $100.0 via PayPalfrom typing import Protocol
class PaymentProcessor(Protocol): """Protocol - structural subtyping"""
def process_payment(self, amount: float) -> bool: """Process a payment""" ...
def refund(self, transaction_id: str) -> bool: """Process a refund""" ...
class CreditCardProcessor: """Implements PaymentProcessor protocol"""
def process_payment(self, amount: float) -> bool: print(f"Processing ${amount} via credit card") return True
def refund(self, transaction_id: str) -> bool: print(f"Refunding transaction {transaction_id} via credit card") return True
# Protocol doesn't enforce - duck typingdef checkout(processor: PaymentProcessor, amount: float): return processor.process_payment(amount)
credit_card = CreditCardProcessor()checkout(credit_card, 100.0) # Works// Interface definitionpublic interface PaymentProcessor { // Abstract methods (implicitly public and abstract) boolean processPayment(double amount); boolean refund(String transactionId);
// Default method (Java 8+) default boolean validateAmount(double amount) { return amount > 0; }
// Static method (Java 8+) static String getProcessorType() { return "Payment Processor"; }}
// Implementationpublic class CreditCardProcessor implements PaymentProcessor { private String apiKey;
public CreditCardProcessor(String apiKey) { this.apiKey = apiKey; }
@Override public boolean processPayment(double amount) { if (!validateAmount(amount)) { return false; } System.out.println("Processing $" + amount + " via credit card"); return true; }
@Override public boolean refund(String transactionId) { System.out.println("Refunding transaction " + transactionId + " via credit card"); return true; }}
public class PayPalProcessor implements PaymentProcessor { @Override public boolean processPayment(double amount) { if (!validateAmount(amount)) { return false; } System.out.println("Processing $" + amount + " via PayPal"); return true; }
@Override public boolean refund(String transactionId) { System.out.println("Refunding transaction " + transactionId + " via PayPal"); return true; }}
// Usage - polymorphismpublic class Main { public static void checkout(PaymentProcessor processor, double amount) { processor.processPayment(amount); }
public static void main(String[] args) { PaymentProcessor creditCard = new CreditCardProcessor("api_key_123"); PaymentProcessor paypal = new PayPalProcessor();
checkout(creditCard, 100.0); // Works checkout(paypal, 100.0); // Works }}Processing $100.0 via credit cardProcessing $100.0 via PayPalMultiple Interface Implementation
Section titled “Multiple Interface Implementation”Classes can implement multiple interfaces:
from abc import ABC, abstractmethod
class Flyable(ABC): @abstractmethod def fly(self): pass
class Swimmable(ABC): @abstractmethod def swim(self): pass
class Duck(Flyable, Swimmable): """Duck implements multiple interfaces"""
def fly(self): return "Flying through the air"
def swim(self): return "Swimming in water"
def quack(self): return "Quack!"
duck = Duck()print(duck.fly()) # "Flying through the air"print(duck.swim()) # "Swimming in water"print(duck.quack()) # "Quack!"Flying through the airSwimming in waterQuack!public interface Flyable { void fly();}
public interface Swimmable { void swim();}
public class Duck implements Flyable, Swimmable { @Override public void fly() { System.out.println("Flying through the air"); }
@Override public void swim() { System.out.println("Swimming in water"); }
public void quack() { System.out.println("Quack!"); }}
// Usagepublic class Main { public static void main(String[] args) { Duck duck = new Duck(); duck.fly(); // "Flying through the air" duck.swim(); // "Swimming in water" duck.quack(); // "Quack!" }}Flying through the airSwimming in waterQuack!Real-World Example: Notification System
Section titled “Real-World Example: Notification System”from abc import ABC, abstractmethod
class NotificationService(ABC): """Interface for notification services"""
@abstractmethod def send(self, recipient: str, message: str) -> bool: """Send notification - must be implemented""" pass
@abstractmethod def can_send(self, recipient: str) -> bool: """Check if notification can be sent""" pass
class EmailService(NotificationService): """Email notification implementation"""
def send(self, recipient: str, message: str) -> bool: if not self.can_send(recipient): return False print(f"Sending email to {recipient}: {message}") return True
def can_send(self, recipient: str) -> bool: return "@" in recipient
class SMSService(NotificationService): """SMS notification implementation"""
def send(self, recipient: str, message: str) -> bool: if not self.can_send(recipient): return False print(f"Sending SMS to {recipient}: {message[:50]}...") return True
def can_send(self, recipient: str) -> bool: return recipient.startswith("+")
class NotificationManager: """Manages multiple notification services"""
def __init__(self): self.services: list[NotificationService] = []
def add_service(self, service: NotificationService): """Add a notification service""" self.services.append(service)
def broadcast(self, recipient: str, message: str): """Send message through all services""" for service in self.services: if service.can_send(recipient): service.send(recipient, message)
# Usagemanager = NotificationManager()manager.add_service(EmailService())manager.add_service(SMSService())
manager.broadcast("user@example.com", "Your order has shipped!")manager.broadcast("+1234567890", "Your order has shipped!")Sending email to user@example.com: Your order has shipped!Sending SMS to +1234567890: Your order has shipped!public interface NotificationService { boolean send(String recipient, String message); boolean canSend(String recipient);}
public class EmailService implements NotificationService { @Override public boolean send(String recipient, String message) { if (!canSend(recipient)) { return false; } System.out.println("Sending email to " + recipient + ": " + message); return true; }
@Override public boolean canSend(String recipient) { return recipient.contains("@"); }}
public class SMSService implements NotificationService { @Override public boolean send(String recipient, String message) { if (!canSend(recipient)) { return false; } System.out.println("Sending SMS to " + recipient + ": " + message.substring(0, Math.min(50, message.length())) + "..."); return true; }
@Override public boolean canSend(String recipient) { return recipient.startsWith("+"); }}
public class NotificationManager { private java.util.List<NotificationService> services;
public NotificationManager() { this.services = new java.util.ArrayList<>(); }
public void addService(NotificationService service) { this.services.add(service); }
public void broadcast(String recipient, String message) { for (NotificationService service : services) { if (service.canSend(recipient)) { service.send(recipient, message); } } }}
// Usagepublic class Main { public static void main(String[] args) { NotificationManager manager = new NotificationManager(); manager.addService(new EmailService()); manager.addService(new SMSService());
manager.broadcast("user@example.com", "Your order has shipped!"); manager.broadcast("+1234567890", "Your order has shipped!"); }}Sending email to user@example.com: Your order has shipped!Sending SMS to +1234567890: Your order has shipped!Visual Representation
Section titled “Visual Representation”Key Takeaways
Section titled “Key Takeaways”When to Use Interfaces
Section titled “When to Use Interfaces”Use interfaces when:
- You want to define a contract that multiple classes can follow
- You need polymorphism - treat different classes the same way
- You want loose coupling - depend on abstractions
- You need multiple inheritance of behavior (not state)
- You want to make code more testable with mock implementations
Examples:
- Payment processors (different payment methods)
- Notification services (email, SMS, push)
- Data access layers (different databases)
- Storage services (local, cloud, database)
- Authentication providers (OAuth, JWT, etc.)