Day 24 — OOP Interview Scenarios (Python)

Concise notes & sample answers for interview preparation — focus on practical OOP design, common scenarios, and Python code examples. Designed for automation/QA engineers preparing for interviews.

Contents
  1. Core OOP Concepts (quick recap)
  2. Common Interview Scenarios & Solutions (with Python)
  3. SOLID Principles (with examples)
  4. Design Patterns you should know
  5. Testing & QA perspective — how to test OOP code
  6. Behavioral / System-design style OOP questions
  7. Mini mock interview: questions + answers
  8. Exercises & Challenges

1. Core OOP Concepts — quick recap

2. Common Interview Scenarios & Solutions (with Python)

Below are frequently asked scenarios. For each: problem, approach, and a compact Python implementation.

2.1 Design a simple BankAccount class (deposit, withdraw, transfer)

class BankAccount:
    def __init__(self, owner: str, balance: float = 0.0):
        self.owner = owner
        self._balance = float(balance)

    @property
    def balance(self):
        return self._balance

    def deposit(self, amount: float):
        if amount <= 0:
            raise ValueError('Deposit must be positive')
        self._balance += amount

    def withdraw(self, amount: float):
        if amount <= 0:
            raise ValueError('Withdraw must be positive')
        if amount > self._balance:
            raise ValueError('Insufficient funds')
        self._balance -= amount

    def transfer_to(self, other_account, amount: float):
        self.withdraw(amount)
        other_account.deposit(amount)

# Usage
# a = BankAccount('Alice', 100)
# b = BankAccount('Bob')
# a.transfer_to(b, 30)

Interview tip: mention thread-safety if asked (use locks for concurrent access) and input validation.

2.2 Implement a Singleton (why and how)

class SingletonMeta(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class Config(metaclass=SingletonMeta):
    def __init__(self):
        self.value = 42

# Usage: only one Config instance will exist

Interviewers might ask when singletons are appropriate — good for shared configuration but can make testing harder. Favor dependency injection for testability.

2.3 Observer / Event system (useful for decoupling)

class Event:
    def __init__(self):
        self._listeners = []
    def subscribe(self, fn):
        self._listeners.append(fn)
    def emit(self, *args, **kwargs):
        for fn in list(self._listeners):
            fn(*args, **kwargs)

# Example usage
# evt = Event()
# evt.subscribe(lambda msg: print('got', msg))
# evt.emit('hello')

2.4 Strategy Pattern (runtime behavior change)

class StrategyA:
    def do(self, data):
        return f'A processed {data}'

class StrategyB:
    def do(self, data):
        return f'B processed {data}'

class Context:
    def __init__(self, strategy):
        self._strategy = strategy
    def run(self, data):
        return self._strategy.do(data)

# context = Context(StrategyA())
# context.run('input')

3. SOLID principles — short Python examples

# Example: SRP — separate logger from processing
class Processor:
    def __init__(self, logger):
        self.logger = logger
    def process(self, data):
        self.logger.log('processing')
        return data.upper()

4. Design Patterns you should know (brief)

Be able to explain where you used them, why chosen, and trade-offs.

5. Testing & QA perspective — how to test OOP code

# pytest example
import pytest

def test_deposit_withdraw():
    from datetime import datetime
    acc = BankAccount('Test', 100)
    acc.deposit(50)
    assert acc.balance == 150
    acc.withdraw(30)
    assert acc.balance == 120
    with pytest.raises(ValueError):
        acc.withdraw(1000)

6. Behavioral / System-design style OOP questions

Examples interviewers ask to evaluate design thinking (not just code):

Approach: clarify requirements, define key classes/interfaces, show sequence of interactions, mention trade-offs (complexity, testing, performance).

7. Mini mock interview — common Q & strong answers

Q: What is the difference between composition and inheritance? When to use which?
A: Inheritance expresses "is-a"; composition expresses "has-a". Prefer composition to reduce coupling and increase flexibility. Use inheritance for polymorphic behaviour and when subclass truly is a subtype.
Q: How would you make a class immutable in Python?
A: Use __slots__ and avoid setters, or return frozen dataclasses: @dataclass(frozen=True). For deep immutability, ensure contained objects are immutable or copied.
Q: How to avoid issues with default mutable arguments?
A: Never use mutable default parameters. Use None and set inside: def __init__(self, lst=None): self.lst = lst or [].

8. Exercises & Challenges

  1. Implement: A LRUCache class with get and put methods (use OrderedDict or doubly-linked list + dict).
  2. Design: A plugin system for a test runner where plugins can register setup/teardown hooks.
  3. Refactor: Given a large God-class that handles DB + validation + report generation, split responsibilities into smaller classes (write the refactored UML).

Mini MCQ (quick self-check)

  1. Which principle suggests classes should have one reason to change? (A) Liskov (B) SRP (C) Open/Closed
  2. Which pattern lets you change an algorithm at runtime? (A) Singleton (B) Strategy (C) Adapter
  3. Why are mutable default args dangerous? (A) Performance (B) Shared state between calls (C) Syntax error