OOD Fundamentals
SOLID Principles
Creational Patterns
Structural Patterns
Behavioral Patterns
Classic OOD Problems: Part 1
Classic OOD Problems: Part 2
Abstract Classes and Interfaces
Why can you not just use regular classes for everything? Because regular classes make promises they should not. When you create a class called Shape with an area() method that returns 0, you are lying to every developer who reads your code. A shape with zero area is not a shape: it is a placeholder pretending to be one. Abstract classes exist to solve this exact problem: they let you define a contract that says "every shape must compute its own area" without providing a fake default.
An abstract class is a class that cannot be instantiated directly. It exists solely as a blueprint for subclasses. It can have two kinds of methods: concrete methods (fully implemented, shared by all subclasses) and abstract methods (declared but not implemented: every subclass must provide its own version).

Why Abstract Methods Matter
Consider a payment system with credit cards, PayPal, and cryptocurrency. All payment methods share common behavior: they all log transactions, they all validate amounts, they all return a receipt. But the actual processing logic is completely different for each one. A credit card charges a card network. PayPal hits an API. Crypto submits a blockchain transaction.
The PaymentMethod class provides validate() and log() as concrete methods: every payment type validates and logs the same way. But execute() is abstract. If you try to instantiate PaymentMethod directly, Python raises TypeError. You must create a subclass that provides execute().
This is the Template Method pattern in action: the abstract class defines the algorithm skeleton (validate then execute then log), and subclasses fill in the variable steps. The base class controls the flow; the subclasses control the specifics.
Abstract classes are not about preventing instantiation: they are about forcing subclasses to make decisions. Every abstract method is a question the base class asks its children: how do you handle this specific responsibility? The compiler enforces that every child answers.
Concrete Methods as Shared Behavior
The real power of abstract classes is combining abstract and concrete methods. The concrete methods provide code reuse: every subclass gets validate() and log() for free. The abstract methods provide customization points: each subclass defines its own execute(). Without abstract classes, you would either duplicate the shared code in every subclass (violating DRY) or use a regular class with a fake default implementation (hiding bugs).
When a Class Should Be Abstract
Make a class abstract when:
- Direct instantiation makes no sense. A generic "Animal" has no meaningful
speak(). Only a Dog or Cat can speak. - Subclasses share behavior but differ in specifics. All database connectors validate queries and pool connections, but each executes queries differently.
- You want the compiler to enforce completeness. If a developer adds a new payment type but forgets to implement
execute(), the code will not compile.
The key insight is that abstract classes capture the parts of a design that are stable (the algorithm skeleton, the shared validations) while leaving the parts that vary (specific processing logic) to subclasses.