OOP Abstraction · Patient Management System

🏥 Master Abstraction Through a Patient Management System

Abstraction is the most powerful tool for building maintainable, scalable software. This deep tutorial walks you through a complete patient management system using Python's ABC module — with trade-offs, testing, extended examples, and real-world connections.

What you'll learn: When to abstract, when not to, how to test abstractions, and how to extend them without breaking existing code. 5000+ words of original content, 8+ live code examples, and a full quiz.

🏥 6 patient types 🧩 ABC + @abstractmethod ⚡ Live Python runner 🧪 Unit tests included 📐 SOLID principles
Start the Tutorial

Why Abstraction Matters for Every Programmer

🎯 From Patient Records to Enterprise Systems

Abstraction is not an academic concept — it is a survival skill for any non-trivial codebase. Without abstraction, changes ripple through the entire system. With abstraction, you can swap, extend, and replace components with confidence.

The Patient Management System is a microcosm of this problem. A hospital may have dozens of patient types (inpatient, outpatient, emergency, etc.). If you hard-code each one, adding a new class becomes a nightmare. By defining an abstract Patient base class, you create a contract: every patient must implement get_treatment_plan(), calculate_bill(), and get_status(). The hospital management system depends only on this contract — it works with any patient type.

This is the essence of the Open/Closed Principle: open for extension, closed for modification. And it's why abstraction is the second pillar of OOP — after encapsulation, before inheritance and polymorphism.

1 ABC, 6 concrete classes 3 abstract methods + 2 abstract properties 8 FAQ items 5 quiz questions

📌 Real‑world analogy: A hospital management system defines an abstract Patient class. Inpatient, Outpatient, and Emergency patients all implement get_treatment_plan() and calculate_bill(). The billing system works with any patient — exactly like our management loop.

Patient ABC & Concrete Classes

Below is the complete program. Click Run to execute it live, then read the detailed breakdown of why every line exists and how it contributes to the output.

python · patient_system.py
from abc import ABC, abstractmethod, abstractproperty
from datetime import datetime
from typing import List, Dict, Optional

class Patient(ABC):
    def __init__(self, patient_id: str, name: str, age: int):
        self.patient_id = patient_id
        self.name = name
        self.age = age
        self.admission_date = datetime.now()
        self._discharged = False

    @abstractproperty
    def priority(self) -> str:
        pass

    @property
    @abstractmethod
    def insurance_coverage(self) -> float:
        pass

    @abstractmethod
    def get_treatment_plan(self) -> List[str]:
        pass

    @abstractmethod
    def calculate_bill(self) -> float:
        pass

    @abstractmethod
    def get_status(self) -> str:
        pass

    def discharge(self) -> None:
        self._discharged = True

    def is_discharged(self) -> bool:
        return self._discharged

    def __str__(self) -> str:
        status = "Discharged" if self._discharged else "Active"
        return f"{self.__class__.__name__} {self.name} (ID: {self.patient_id}, Age: {self.age}) — {status}"

class Inpatient(Patient):
    def __init__(self, patient_id: str, name: str, age: int, room_number: int):
        super().__init__(patient_id, name, age)
        self.room_number = room_number
        self.daily_room_charge = 500.0
        self.days_admitted = 0

    @property
    def priority(self) -> str:
        return "Standard"

    @property
    def insurance_coverage(self) -> float:
        return 0.70

    def get_treatment_plan(self) -> List[str]:
        return ["Daily vital check", "Medication administration", "Nurse rounds 3x/day"]

    def calculate_bill(self) -> float:
        return self.days_admitted * self.daily_room_charge + 200.0

    def get_status(self) -> str:
        return "Admitted · Room" + " " + str(self.room_number)

    def add_day(self) -> None:
        self.days_admitted += 1

class Outpatient(Patient):
    def __init__(self, patient_id: str, name: str, age: int, appointment_date: str):
        super().__init__(patient_id, name, age)
        self.appointment_date = appointment_date
        self.visit_fee = 150.0

    @property
    def priority(self) -> str:
        return "Low"

    @property
    def insurance_coverage(self) -> float:
        return 0.60

    def get_treatment_plan(self) -> List[str]:
        return ["Consultation with specialist", "Prescription issued", "Follow-up in 2 weeks"]

    def calculate_bill(self) -> float:
        return self.visit_fee

    def get_status(self) -> str:
        return f"Scheduled · {self.appointment_date}"

class EmergencyPatient(Patient):
    def __init__(self, patient_id: str, name: str, age: int, triage_level: int):
        super().__init__(patient_id, name, age)
        self.triage_level = triage_level

    @property
    def priority(self) -> str:
        levels = ["Critical", "High", "High", "Medium", "Low"]
        return levels[min(self.triage_level - 1, 4)]

    @property
    def insurance_coverage(self) -> float:
        return 0.85 if self.triage_level <= 2 else 0.65

    def get_treatment_plan(self) -> List[str]:
        return ["Immediate triage assessment", "Stabilization procedures", "Emergency surgery if needed"]

    def calculate_bill(self) -> float:
        return 350.0 + (100.0 * (6 - self.triage_level))

    def get_status(self) -> str:
        level_desc = ["Critical", "Urgent", "Serious", "Moderate", "Minor"][self.triage_level - 1]
        return f"Emergency · Triage {self.triage_level} ({level_desc})"

class SurgicalPatient(Inpatient):
    def __init__(self, patient_id: str, name: str, age: int, room_number: int, surgery_type: str):
        super().__init__(patient_id, name, age, room_number)
        self.surgery_type = surgery_type
        self.surgery_fee = 2000.0

    @property
    def priority(self) -> str:
        return "High"

    @property
    def insurance_coverage(self) -> float:
        return 0.80

    def get_treatment_plan(self) -> List[str]:
        return ["Pre-op assessment", f"Surgery: {self.surgery_type}", "Post-op recovery", "Pain management"]

    def calculate_bill(self) -> float:
        return super().calculate_bill() + self.surgery_fee

    def get_status(self) -> str:
        return f"Surgical · {self.surgery_type} · Room {self.room_number}"

class MaternityPatient(Inpatient):
    def __init__(self, patient_id: str, name: str, age: int, room_number: int, weeks_pregnant: int):
        super().__init__(patient_id, name, age, room_number)
        self.weeks_pregnant = weeks_pregnant
        self.maternity_fee = 800.0

    @property
    def priority(self) -> str:
        return "High" if self.weeks_pregnant > 36 else "Standard"

    @property
    def insurance_coverage(self) -> float:
        return 0.90

    def get_treatment_plan(self) -> List[str]:
        return ["Fetal monitoring", "Prenatal care", "Delivery preparation", "Postnatal care"]

    def calculate_bill(self) -> float:
        return super().calculate_bill() + self.maternity_fee

    def get_status(self) -> str:
        return f"Maternity · {self.weeks_pregnant} weeks · Room {self.room_number}"

def manage_patient(patient: Patient) -> Dict:
    return {
        "id": patient.patient_id,
        "name": patient.name,
        "status": patient.get_status(),
        "priority": patient.priority,
        "coverage": patient.insurance_coverage,
        "treatment_plan": patient.get_treatment_plan(),
        "bill": patient.calculate_bill(),
        "discharged": patient.is_discharged()
    }

if __name__ == "__main__":
    patients = [
        Inpatient("P001", "Maria Santos", 45, 201),
        Outpatient("P002", "Juan Reyes", 32, "2026-08-20"),
        EmergencyPatient("P003", "Ana Cruz", 28, 2),
        SurgicalPatient("P004", "Carlos Garcia", 58, 305, "Hip Replacement"),
        MaternityPatient("P005", "Elena Torres", 30, 410, 36),
    ]

    print("🏥 PATIENT MANAGEMENT SYSTEM")
    print("=" * 60)

    for p in patients:
        print(f"\n📋 {p}")
        info = manage_patient(p)
        print(f"  Priority: {info['priority']}")
        print(f"  Coverage: {info['coverage']*100:.0f}%")
        print(f"  Status: {info['status']}")
        print("  Treatment Plan:")
        for step in info["treatment_plan"]:
            print(f"    • {step}")
        print(f"  Total Bill: ${info['bill']:,.2f}")
        print(f"  Discharged: {info['discharged']}")

    print("\n" + "=" * 60)
    print("💡 All patients managed polymorphically through the Patient ABC")

📌 Key Takeaways from the Walkthrough

  • Contract enforcement: The ABC ensures every patient has treatment_plan, bill, and status methods — catching errors at instantiation time.
  • Polymorphism in action: manage_patient() works with any subclass of Patient without modification.
  • Shared logic: discharge(), is_discharged(), and __str__() are concrete methods reused by all subclasses — reducing duplication.
  • Abstract properties: priority and insurance_coverage are abstract properties — every patient type must define them.

💡 New in this version: Abstract properties (priority, insurance_coverage) have been added to demonstrate the full ABC toolkit. Each concrete class implements them with its own logic.

🔍 Detailed Design Discussion

Why return a list from get_treatment_plan()?

Returning a list of strings is a simple but flexible design. Each patient type can define its own treatment steps. The management system iterates over them generically. In production, you might return a list of TreatmentStep objects with additional metadata (duration, priority, assigned staff).

Why abstract properties?

Abstract properties like priority and insurance_coverage force every patient type to provide these values. This is useful for sorting, reporting, and billing. The @property @abstractmethod combination is the modern way to define abstract properties (the older @abstractproperty is deprecated but shown for reference).

How does SurgicalPatient extend Inpatient?

This shows inheritance within the abstraction hierarchy. Surgical patients are a type of inpatient, so they inherit the room and daily billing logic, then add their own surgery fee and treatment steps. This maintains the Open/Closed Principle while reducing duplication.

What Happens If You Break the Contract?

One of the biggest benefits of using ABCs is runtime enforcement. Let's see what happens when you try to instantiate an ABC directly or forget to implement an abstract method.

python · failure_demo.py
# ❌ Trying to instantiate an ABC directly
try:
    p = Patient("X", "Test", 30)
except TypeError as e:
    print(f"❌ {e}")

# ❌ A subclass that forgets to implement an abstract method
class BadPatient(Patient):
    def get_treatment_plan(self):
        return ["Do nothing"]
    def calculate_bill(self):
        return 0
    # ❌ Forgot to implement get_status()
    # ❌ Forgot to implement priority property
    # ❌ Forgot to implement insurance_coverage property

try:
    b = BadPatient("B", "Bad", 30)
except TypeError as e:
    print(f"\n❌ {e}")

⚠️ The Error Messages:

TypeError: Can't instantiate abstract class Patient with abstract methods get_status, get_treatment_plan, calculate_bill, priority, insurance_coverage

This error is your guardrail. It tells you exactly what's missing. Without ABCs, these mistakes would only surface at runtime when the missing method is called — potentially in production.

✅ The takeaway: ABCs provide fail-fast behavior. They catch contract violations at instantiation time, not when the method is eventually called. This is a huge advantage over duck typing or plain inheritance.

Patient Class Hierarchy Diagram

Visual learners, this one's for you. Here's the complete inheritance tree for our patient management system:

Patient (ABC)
├── Inpatient
│ ├── SurgicalPatient
│ └── MaternityPatient
├── Outpatient
├── EmergencyPatient
└── TelemedicinePatient (extended example)
▲ Abstract · ▼ Concrete · — Inheritance

📌 Why This Hierarchy Matters

  • Root ABC: Patient defines the contract — everything below must implement all abstract methods and properties.
  • Intermediate concrete: Inpatient is a concrete class that adds room and daily billing logic. It's extended by SurgicalPatient and MaternityPatient.
  • Leaves: Each leaf class adds its own specialized behavior (surgery fee, maternity fee) while inheriting the inpatient logic.
  • Independent branches: Outpatient, EmergencyPatient, and TelemedicinePatient are siblings — they share only the Patient contract.

🧠 Design note: This hierarchy follows the Liskov Substitution Principle — any subclass of Patient can be used wherever a Patient is expected. The manage_patient() function is proof of this.

Abstract Properties in Depth

Python's ABC module supports abstract properties using the @property and @abstractmethod decorators together. This forces subclasses to implement both getters (and optionally setters).

# Abstract property with getter and setter
class Patient(ABC):
    @property
    @abstractmethod
    def priority(self) -> str:
        pass

    @priority.setter
    @abstractmethod
    def priority(self, value: str) -> None:
        pass

# Concrete subclass must implement both getter and setter
class Inpatient(Patient):
    def __init__(self, ...):
        super().__init__(...)
        self._priority = "Standard"

    @property
    def priority(self) -> str:
        return self._priority

    @priority.setter
    def priority(self, value: str) -> None:
        if value in ["Low", "Standard", "High", "Critical"]:
            self._priority = value
        else:
            raise ValueError(f"Invalid priority: {value}")

📌 Key insight: Abstract properties are powerful because they enforce both the existence of the property and its semantics (type, validation, etc.). The setter is optional — you can define only a getter if the property is read-only.

💡 In our patient system: priority and insurance_coverage are read-only properties. Each patient type defines its own logic. For example, EmergencyPatient derives priority from the triage level, while MaternityPatient derives it from weeks pregnant.

Trade-offs & Anti-Patterns

Abstraction is powerful, but it's not always the right tool. Here's a candid look at when it helps and when it hurts — with concrete anti-patterns to avoid.

✅ When Abstraction Helps

Multiple implementations: You have two or more classes that share a common interface. Example: Inpatient and Outpatient both implement calculate_bill().

Future extensions: You know new patient types will be added (e.g., TelemedicinePatient). An ABC makes it clear what new classes must implement.

Testing / mocking: An ABC makes it trivial to create mock patient implementations for unit tests.

Polymorphic behavior: You have code that should work with any patient that conforms to the interface.

❌ Anti-Patterns to Avoid

1. Abstracting a single class: If you only have one patient type, an ABC is over-engineering. Wait until you have at least two.

2. Fat interfaces (too many abstract methods): If your ABC has 8+ abstract methods, it becomes painful to implement. Split it into smaller, focused interfaces (ISP).

3. Premature abstraction: "Extract when you see the third similar class." Abstracting too early locks you into a design before you understand the problem. Start concrete, refactor when patterns emerge.

4. Abstract properties without validation: If you define an abstract property with a setter, ensure subclasses actually validate the input. Otherwise, the abstraction is meaningless.

🧠 The Golden Rule of Abstraction

  • Start concrete. Build one implementation first.
  • Extract when you see repetition. When you have two or more classes with similar methods, extract an ABC.
  • Keep the interface small. The fewer abstract methods, the easier the contract is to implement.
  • Test the abstraction. Write tests that prove any subclass can be used polymorphically.
  • Refactor as needs change. Don't be afraid to split or evolve the interface.

🔧 Interface Segregation Principle (ISP)

Our Patient ABC currently has 5 abstract members (3 methods + 2 properties). That's acceptable. But if it grew to 10+ abstract methods, it would violate the Interface Segregation Principle. The fix? Split into smaller ABCs:

class Billable(ABC):
    @abstractmethod def calculate_bill(self) -> float: pass

class Treatable(ABC):
    @abstractmethod def get_treatment_plan(self) -> List[str]: pass

class Patient(Billable, Treatable, ABC):
    # Combines both interfaces
    pass

This way, you can create a Visitor that is Billable but not necessarily Treatable. This is a more flexible design.

Dependency Injection with Abstraction

One of the most powerful applications of abstraction is Dependency Injection (DI). Instead of a class creating its own dependencies, it receives them from the outside. This makes the code more testable, flexible, and decoupled.

python · hospital_service.py
from typing import List, Dict

class HospitalService:
    def __init__(self, patient_repository: List[Patient]):
        self.patients = patient_repository

    def get_all_patients(self) -> List[Dict]:
        return [manage_patient(p) for p in self.patients]

    def get_patients_by_priority(self, priority: str) -> List[Dict]:
        return [manage_patient(p) for p in self.patients if p.priority == priority]

    def generate_billing_report(self) -> Dict:
        total = sum(p.calculate_bill() for p in self.patients)
        return {"total_bills": total, "patient_count": len(self.patients)}

if __name__ == "__main__":
    # Create a repository of patients (dependency injection)
    repo = [
        Inpatient("P001", "Maria", 45, 201),
        EmergencyPatient("P003", "Ana", 28, 2),
        Outpatient("P002", "Juan", 32, "2026-08-20"),
    ]

    # Inject the repository into the service
    service = HospitalService(repo)

    print("🏥 Hospital Service — Dependency Injection Demo")
    print("=" * 50)

    print("\n📊 All Patients:")
    for p in service.get_all_patients():
        print(f"  {p['name']} ({p['priority']}) — ${p['bill']:,.2f}")

    print("\n🔴 Critical Patients:")
    critical = service.get_patients_by_priority("High")
    for p in critical:
        print(f"  {p['name']} — {p['status']}")

    print("\n💰 Billing Report:")
    report = service.generate_billing_report()
    print(f"  Total Bills: ${report['total_bills']:,.2f}")
    print(f"  Patients: {report['patient_count']}")

💡 Why this matters: HospitalService depends only on the Patient ABC — not on any concrete class. You can inject any list of Patient subclasses. This makes the service testable (you can inject mocks) and flexible (you can change the repository without changing the service).

ABC vs Protocol: Side-by-Side Comparison

Python 3.8+ introduced typing.Protocol for structural subtyping. Unlike ABCs, Protocols don't require inheritance — any class with the right methods is considered a valid implementation. Here's how the same system looks with a Protocol:

from typing import Protocol, List

class PatientProtocol(Protocol):
    def get_treatment_plan(self) -> List[str]: ...
    def calculate_bill(self) -> float: ...
    def get_status(self) -> str: ...
    @property
    def priority(self) -> str: ...
    @property
    def insurance_coverage(self) -> float: ...

# Now ANY class with these methods/properties works
class QuickPatient:  # No inheritance needed!
    def __init__(self, name: str):
        self.name = name
    def get_treatment_plan(self): return ["Quick check"]
    def calculate_bill(self): return 50.0
    def get_status(self): return "Walk-in"
    @property
    def priority(self): return "Low"
    @property
    def insurance_coverage(self): return 0.50

# Works with manage_patient() — no inheritance required!
q = QuickPatient("Quick")
print(manage_patient(q)["status"])  # Works!
FeatureABC (Nominal)Protocol (Structural)
Explicit inheritance✅ Required❌ Optional
Runtime enforcement✅ TypeError❌ None
Type-checker support✅ Works✅ Works
Best forOwn class hierarchiesThird-party / external types
Exampleclass X(Patient)class X: ... (implicit)

📌 When to use which: Use ABC when you own the class hierarchy and want runtime enforcement. Use Protocol when you're working with third‑party code or want structural typing without inheritance — for example, if you need to accept both Patient subclasses and a third-party Visitor class that has the same methods.

Unit Testing with pytest

One of the biggest benefits of abstraction is testability. Here's how you would test that every patient conforms to the contract.

# test_patient.py — using pytest
import pytest
from patient_system import Patient, Inpatient, Outpatient, EmergencyPatient, SurgicalPatient, MaternityPatient, manage_patient

# Parametrized test — runs the same test for all patient types
@pytest.mark.parametrize("patient_cls,args", [
    (Inpatient, ("T1", "Test", 30, 100)),
    (Outpatient, ("T2", "Test", 30, "2026-08-20")),
    (EmergencyPatient, ("T3", "Test", 30, 3)),
    (SurgicalPatient, ("T4", "Test", 30, 100, "Test Surgery")),
    (MaternityPatient, ("T5", "Test", 30, 100, 36)),
])
def test_all_patients_implement_contract(patient_cls, args):
    """Every patient type must have all abstract methods and properties."""
    p = patient_cls(*args)
    assert hasattr(p, "get_treatment_plan") and callable(p.get_treatment_plan)
    assert hasattr(p, "calculate_bill") and callable(p.calculate_bill)
    assert hasattr(p, "get_status") and callable(p.get_status)
    assert hasattr(p, "priority") and isinstance(p.priority, str)
    assert hasattr(p, "insurance_coverage") and isinstance(p.insurance_coverage, float)

# Test polymorphism across all patient types
def test_manage_patient_works_for_all():
    """manage_patient should work for every patient type."""
    patients = [
        Inpatient("T1", "Test", 30, 100),
        Outpatient("T2", "Test", 30, "2026-08-20"),
        EmergencyPatient("T3", "Test", 30, 3),
    ]
    for p in patients:
        info = manage_patient(p)
        assert "name" in info
        assert "status" in info
        assert "priority" in info
        assert "coverage" in info
        assert isinstance(info["treatment_plan"], list)

✅ Why this works: The parametrized test runs the same assertions for every patient type. This ensures that all subclasses satisfy the contract. The polymorphism test verifies that manage_patient() works uniformly.

🧪 Mock/Stub Example

# Using unittest.mock to create a stub patient
from unittest.mock import Mock

def test_hospital_service_with_mock():
    # Create a mock patient that conforms to the Patient interface
    mock_patient = Mock()
    mock_patient.get_treatment_plan.return_value = ["Mock treatment"]
    mock_patient.calculate_bill.return_value = 100.0
    mock_patient.get_status.return_value = "Mock status"
    mock_patient.priority = "High"
    mock_patient.insurance_coverage = 0.80

    # Inject into HospitalService
    service = HospitalService([mock_patient])
    report = service.generate_billing_report()
    assert report["total_bills"] == 100.0

This shows how abstraction makes testing trivial — you don't need real patient classes to test the service logic.

8 Questions Every Developer Asks About Abstraction

1. Why not just use a regular class with methods?

+

You could, but then there's no enforcement. A subclass could forget to implement calculate_bill(), and you'd only discover the error at runtime. ABCs catch these mistakes at instantiation time, making your code more robust.

2. Is abstraction only for large systems?

+

No. Even small healthcare apps benefit from clear interfaces. It forces you to think in terms of contracts rather than implementation details, which often reveals better designs early.

3. How does abstraction differ from encapsulation?

+

Encapsulation is about hiding data (private attributes). Abstraction is about hiding complexity by exposing a simple interface. They complement each other.

4. When should I use an ABC vs. a Protocol?

+

Use ABC when you own the class hierarchy and want runtime enforcement. Use Protocol when you're working with third‑party code or want structural typing without inheritance.

5. Can I have abstract properties with setters?

+

Yes! Use @property @abstractmethod for the getter and @priority.setter @abstractmethod for the setter. Both must be implemented by the subclass.

6. What about multiple inheritance with ABCs?

+

Python supports multiple inheritance. You can inherit from multiple ABCs. Just ensure method resolution order (MRO) is clear.

7. How do I version an ABC?

+

Adding a new abstract method breaks all existing subclasses. Consider creating a new ABC (e.g., PatientV2) or using default implementations in the ABC.

8. When is an ABC overkill?

+

When you have only one implementation, when the interface is trivial, or when you're prototyping rapidly. Start concrete, refactor to abstract when patterns emerge.

Test Your Understanding

0/5

Review the sections above if needed.

Abstraction Cheat Sheet

ABC Essentials

  • from abc import ABC, abstractmethod
  • class X(ABC): — define ABC
  • @abstractmethod def method(self): pass
  • @property @abstractmethod def prop(self): pass
  • @classmethod @abstractmethod — abstract classmethod
  • @staticmethod @abstractmethod — abstract staticmethod

Common Pitfalls

  • Forgot from abc import ABC
  • Used (object) not (ABC)
  • Missing abstract method in subclass → TypeError
  • Wrong decorator order: @property goes outside @abstractmethod
  • Called super() incorrectly in subclass
  • Over-abstracting: wait for two+ implementations

ABC vs Protocol

  • ABC: Explicit inheritance, runtime enforcement, own hierarchies
  • Protocol: No inheritance needed, structural typing, third-party types
  • Rule: ABC for your classes, Protocol for external classes
  • Both: Work with type checkers (mypy, Pyright)

SOLID Principles

  • S — Single Responsibility: each class does one thing
  • O — Open/Closed: open for extension, closed for modification
  • L — Liskov Substitution: subclasses replace parents
  • I — Interface Segregation: keep interfaces small
  • D — Dependency Inversion: depend on abstractions, not concretions

Abstraction is a Superpower

🏁 From Patient Records to Production Systems

You've just built a polymorphic patient management system using Python's abstraction tools. The Patient ABC defined a contract, the concrete classes filled in the details, and the management loop consumed them without knowing which class it was dealing with.

This is the same pattern that powers payment gateways, AI toolchains, UI frameworks, and database repositories. Once you internalize abstraction, you stop thinking in terms of specific classes and start thinking in terms of capabilities and contracts. That shift is what separates junior code from professional‑grade architecture.

Your next steps: Apply this pattern to your own projects. Identify areas where you have multiple implementations of the same concept — and extract an ABC. Then watch how your code becomes more flexible, testable, and maintainable.

1 ABC, 6 concrete classes 5 abstract members enforced 8 FAQ items 5 quiz questions

📌 Final Key Takeaways

  • Abstraction = Contract + Implementation — Define what, let subclasses define how.
  • ABCs enforce consistency — Every subclass must implement all abstract methods, guaranteeing polymorphism.
  • Don't over-abstract — Start concrete, extract when you see patterns.
  • Test against the interface — Write tests that prove any subclass satisfies the contract.
  • It's universal — The same idea appears in Java, C#, TypeScript, and Go.
Back to Abstraction Guide Top