The Vault and the Concierge: OOP That Actually Makes Sense

Junior/Mid Engineer Asked at: All of them. Foundational knowledge.

Q: Model a simple library system in Python. Create a `Book` class that protects its core data, and an `EBook` class that inherits from it and extends its functionality. Explain your design choices around encapsulation, abstraction, and inheritance.

Why this matters: They aren't asking you to recite textbook definitions. This is a design question. They want to see if you can build small, robust, and understandable components. Your answer reveals whether you write code that creates clarity or code that creates complexity.

Interview frequency: High. A classic way to test object-oriented thinking.

❌ The Death Trap

The candidate creates a simple "data bag"—a class with public attributes and no methods. It's a glorified dictionary that offers no protection, no abstraction, and no clear contract.

"The naive approach:"

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

my_book = Book('The Hobbit', 'J.R.R. Tolkien')
# Another part of the codebase can easily corrupt the data.
my_book.title = 12345  # Catastrophic failure waiting to happen

This provides zero guarantees. Any part of the program can mutate the book's title into nonsense. This design is fragile because it trusts every other line of code in the entire system to behave perfectly.

🔄 The Reframe

What they're really asking: "Can you build a component that makes a promise and keeps it? Show me you can design a stable interface that protects its own internal state, making the entire system easier to reason about."

This reveals if you think in terms of contracts and APIs, even for internal code. It shows you're building for maintainability and trying to reduce cognitive load for the next developer who has to touch your code.

🧠 The Mental Model

I call this **"The Vault and the Concierge."**

Vault Encapsulation The core data (`_title`, `_author`) is locked in a vault. The internal state is protected and cannot be changed arbitrarily from the outside. This guarantees data integrity.
Concierge Abstraction You interact with a friendly, professional concierge (the object's public methods). You ask for a "book summary" (`get_display_info()`). The concierge handles the complex internal process of retrieving and formatting the data for you, hiding the messy details.
Family Business Inheritance A new, specialized business (`EBook`) opens up. It's part of the family, so it inherits all the standard operations and vault access of the original business (`Book`), but adds its own special services.

✅ The Answer

"My approach is to design these classes as reliable components that make strong guarantees. I'll use encapsulation to protect the data, abstraction to provide a clean interface, and inheritance to reuse code and model the 'is-a' relationship."

Task 1: The `Book` Class (Encapsulation & Abstraction)

class Book:
    """Represents a physical book with protected core attributes."""
    
    def __init__(self, title: str, author: str, isbn: str):
        # --- The Vault ---
        # These are "private" by convention. The underscore signals to other
        # developers: "This is an internal detail, do not touch it."
        self._title = title
        self._author = author
        self._isbn = isbn

    # --- The Concierge's Public Desk ---
    @property
    def title(self) -> str:
        """Provides safe, read-only access to the title."""
        return self._title

    @property
    def author(self) -> str:
        return self._author

    @property
    def isbn(self) -> str:
        return self._isbn

    def get_display_info(self) -> str:
        """
        Abstraction in action: The user doesn't care HOW this string
        is formatted, only that they can reliably get it.
        """
        return f'"{self._title}" by {self._author} (ISBN: {self._isbn})'

This design makes a promise: once a `Book` object is created, its core identity (`title`, `author`, `isbn`) cannot be changed. The `@property` decorator gives us the clean `book.title` syntax for reading, but it will raise an `AttributeError` if you try to write to it, thus protecting our vault.

Task 2: The `EBook` Class (Inheritance)

class EBook(Book): # EBook is a type of Book
    """Represents an electronic book, inheriting from Book."""

    def __init__(self, title: str, author: str, isbn: str, file_size_mb: float):
        # Ask the parent class (the original business) to do its setup work.
        super().__init__(title, author, isbn)
        
        # Add the new, specific attribute for EBook's vault.
        self._file_size_mb = file_size_mb

    @property
    def file_size_mb(self) -> float:
        return self._file_size_mb

    # Override the parent's method to provide a more specific version.
    def get_display_info(self) -> str:
        # Reuse the parent's formatting logic to avoid duplicating code.
        base_info = super().get_display_info()
        return f"{base_info} [eBook, {self._file_size_mb:.1f}MB]"

# --- Example Usage ---
physical_book = Book("Dune", "Frank Herbert", "978-0441013593")
ebook = EBook("Neuromancer", "William Gibson", "978-0441569595", 2.5)

print(physical_book.get_display_info())
print(ebook.get_display_info())

The `EBook` class reuses all the logic and protections of `Book` via `super()` and then adds its own specialization. This is the essence of building clean, hierarchical systems.

🎯 The Memorable Hook

This reframes OOP from a set of rules into a philosophy of building trustworthy components. It shows you think about creating reliable systems, not just functional ones.

💭 Inevitable Follow-ups

Q: "How would Python's `@dataclass(frozen=True)` simplify your `Book` class?"

Be ready: "It's an excellent modern alternative for data-centric objects. `@dataclass` would automatically generate the `__init__` method, and `frozen=True` would make instances immutable, achieving the same data protection as my property-based solution with far less boilerplate code. I'd use it for any object that primarily serves as a structured data container."

Q: "Why use a single underscore `_title` instead of a double underscore `__title`?"

Be ready: "Double underscore invokes 'name mangling,' which is a mechanism to avoid naming collisions in subclasses, not a true privacy feature. The strong community convention in Python is to use a single underscore to signal 'this is internal, please don't rely on it.' It's a contract based on convention and trust, which is very Pythonic."

🔄 Adapt This Framework

If you're junior: Nailing the `Book` class with private-by-convention fields and `@property` decorators is a huge win. Clearly explaining the "why" behind protecting the data shows you're thinking beyond the syntax.

If you're senior: You should quickly implement the solution and then proactively discuss the trade-offs. Compare this manual approach to using `@dataclass` or libraries like `Pydantic` for data validation. Discuss how this interface design makes testing easier (e.g., you can mock `get_display_info`).

Written by Benito J D