Lesson 12 / 25
Programming to Interfaces and Duck Typing
Depend on abstractions and compare nominal interfaces with duck typing.
Depend on what, not on who
Programming to an interface means declaring variables, parameters and return types using abstract types (List, PaymentGateway) rather than concrete classes (ArrayList, RazorpayGateway). Code written against the interface works with any implementation, so you can swap implementations, add new ones and substitute test doubles without changing callers. Java and C# use nominal typing: a class must explicitly declare implements PaymentGateway. Python and JavaScript use duck typing ("if it walks like a duck and quacks like a duck"): any object with the right methods works, checked only when called. Python adds optional structure with abstract base classes (abc.ABC) and protocols (typing.Protocol), which type checkers verify structurally. Go interfaces are also structural: a type satisfies an interface simply by having its methods. Whatever the language, keep interfaces small and focused on what callers need.
A payment interface, two implementations and a Python protocol
Checkout depends only on the abstraction.
public interface PaymentGateway {
PaymentResult charge(String orderId, long amountPaise);
}
public final class UpiGateway implements PaymentGateway {
public PaymentResult charge(String orderId, long amountPaise) { /* call UPI provider */ return PaymentResult.ok(); }
}
public final class FakeGateway implements PaymentGateway { // for tests
public PaymentResult charge(String orderId, long amountPaise) { return PaymentResult.ok(); }
}
public final class Checkout {
private final PaymentGateway gateway; // interface type
public Checkout(PaymentGateway gateway) { this.gateway = gateway; }
public PaymentResult pay(String orderId, long amount) { return gateway.charge(orderId, amount); }
}
// Python: structural typing with a Protocol
// from typing import Protocol
// class PaymentGateway(Protocol):
// def charge(self, order_id: str, amount_paise: int) -> bool: ...
// any class with a matching charge() method is accepted by a type checkerInterfaces belong to the caller
Design an interface from the point of view of the code that uses it, with only the methods it needs. A 30-method interface that mirrors one implementation is just a header file, not an abstraction.
Quick check: What is duck typing?
- An object is usable if it has the required methods, regardless of its declared type
- A class must declare which interfaces it implements
- Only primitive types can be used
- A naming convention for Python classes
Answer
An object is usable if it has the required methods, regardless of its declared type — Duck typing checks behaviour (available methods) rather than declared type.