Bird
Raised Fist0
Interview Prepoop-design-patternsmediumAmazonGoogleMicrosoftFlipkartSwiggy

Template Method Pattern - Define Skeleton, Override Steps

Choose your preparation mode3 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
🎯
Template Method Pattern - Define Skeleton, Override Steps
mediumOOPAmazonGoogleMicrosoft

Imagine a cooking show where the host follows a fixed recipe outline but changes the ingredients and cooking style each episode. This is the essence of the Template Method Pattern.

💡 This problem introduces a design pattern that helps organize code by defining a fixed algorithm structure while allowing subclasses to customize specific steps. Beginners often struggle because they try to override the whole process instead of just parts, missing the power of code reuse and controlled extension.
📋
Problem Statement

Design a base class that defines the skeleton of an algorithm in a method, deferring some steps to subclasses. Subclasses should override these steps without changing the algorithm's structure. Implement this pattern demonstrating how the base class controls the algorithm flow and subclasses customize behavior.

The base class must define the algorithm skeleton as a template method.Subclasses can override one or more steps but cannot change the template method itself.The solution should demonstrate at least one hook method (optional step).Use inheritance and method overriding to implement the pattern.
💡
Example
Input"Base class defines 'prepareRecipe' method calling steps: boilWater, brew, pourInCup, addCondiments. Subclass 'Tea' overrides brew and addCondiments. Subclass 'Coffee' overrides brew and addCondiments differently."
OutputCalling prepareRecipe on Tea prints steps for making tea; calling on Coffee prints steps for making coffee.

The base class controls the sequence, subclasses customize brewing and condiments without changing the overall flow.

  • Subclass overrides no steps → output is default base class behavior
  • Subclass overrides all steps → output fully customized but sequence preserved
  • Subclass overrides hook method to skip optional step → optional step omitted
  • Multiple subclasses with different overrides → each produces distinct output
⚠️
Common Mistakes
Overriding the template method in subclass

Algorithm flow breaks, inconsistent behavior

Make template method final or document not to override

Duplicating common steps in subclasses

Code duplication and maintenance issues

Put common steps in base class methods called by template method

Not using abstract methods for variable steps

Subclasses may not override needed steps, causing runtime errors or default behavior

Declare abstract methods or throw errors in base class

Ignoring hook methods for optional behavior

Subclasses override entire template method to skip steps, breaking structure

Use hook methods to control optional steps cleanly

Mixing inheritance with unrelated responsibilities

Violates single responsibility principle, hard to maintain

Keep base class focused on algorithm skeleton only

🧠
Brute Force (Direct Implementation Without Template Method)
💡 Starting with a naive approach helps understand the problem by showing what happens without the pattern - code duplication and fragile structure.

Intuition

Implement each algorithm variant fully in separate classes without shared structure, leading to repeated code and inconsistent flow.

Algorithm

  1. Create separate classes for each algorithm variant.
  2. Implement the entire algorithm in each class independently.
  3. Duplicate common steps across classes.
  4. Use these classes directly without shared base.
💡 This approach is straightforward but quickly becomes unmanageable as variants grow.
</>
Code
class Tea:
    def prepare_recipe(self):
        print('Boil water')
        print('Steep tea bag')
        print('Pour into cup')
        print('Add lemon')

class Coffee:
    def prepare_recipe(self):
        print('Boil water')
        print('Brew coffee grounds')
        print('Pour into cup')
        print('Add sugar and milk')

if __name__ == '__main__':
    print('Making Tea:')
    Tea().prepare_recipe()
    print()
    print('Making Coffee:')
    Coffee().prepare_recipe()
Line Notes
class Tea:Defines a class for tea preparation without reuse to illustrate code duplication
def prepare_recipe(self):Implements the full algorithm for tea without shared structure, showing naive approach
print('Boil water')Common step duplicated in both classes, highlighting repetition
if __name__ == '__main__':Driver code to demonstrate usage and output of naive approach
class Tea {
    void prepareRecipe() {
        System.out.println("Boil water");
        System.out.println("Steep tea bag");
        System.out.println("Pour into cup");
        System.out.println("Add lemon");
    }
}

class Coffee {
    void prepareRecipe() {
        System.out.println("Boil water");
        System.out.println("Brew coffee grounds");
        System.out.println("Pour into cup");
        System.out.println("Add sugar and milk");
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("Making Tea:");
        new Tea().prepareRecipe();
        System.out.println();
        System.out.println("Making Coffee:");
        new Coffee().prepareRecipe();
    }
}
Line Notes
class Tea {Separate class for tea without shared base to show naive implementation
void prepareRecipe() {Full algorithm implemented here, no reuse or abstraction
System.out.println("Boil water");Common step duplicated in both classes, illustrating repetition
public static void main(String[] args) {Main method to run examples and show output
#include <iostream>
using namespace std;

class Tea {
public:
    void prepareRecipe() {
        cout << "Boil water" << endl;
        cout << "Steep tea bag" << endl;
        cout << "Pour into cup" << endl;
        cout << "Add lemon" << endl;
    }
};

class Coffee {
public:
    void prepareRecipe() {
        cout << "Boil water" << endl;
        cout << "Brew coffee grounds" << endl;
        cout << "Pour into cup" << endl;
        cout << "Add sugar and milk" << endl;
    }
};

int main() {
    cout << "Making Tea:" << endl;
    Tea tea;
    tea.prepareRecipe();
    cout << endl;
    cout << "Making Coffee:" << endl;
    Coffee coffee;
    coffee.prepareRecipe();
    return 0;
}
Line Notes
class Tea {Defines tea class with full algorithm, no reuse
void prepareRecipe() {Implements all steps for tea independently
cout << "Boil water" << endl;Duplicated common step in both classes
int main() {Driver code to demonstrate usage and output
class Tea {
    prepareRecipe() {
        console.log('Boil water');
        console.log('Steep tea bag');
        console.log('Pour into cup');
        console.log('Add lemon');
    }
}

class Coffee {
    prepareRecipe() {
        console.log('Boil water');
        console.log('Brew coffee grounds');
        console.log('Pour into cup');
        console.log('Add sugar and milk');
    }
}

console.log('Making Tea:');
new Tea().prepareRecipe();
console.log();
console.log('Making Coffee:');
new Coffee().prepareRecipe();
Line Notes
class Tea {Separate class for tea without reuse to illustrate naive approach
prepareRecipe() {Full algorithm implemented here without shared structure
console.log('Boil water');Common step duplicated in both classes
console.log('Making Tea:');Driver code to show output of naive approach
Complexity
TimeO(1) per call
SpaceO(1) per call

Each method runs a fixed number of print statements; runtime is constant. However, code duplication grows linearly with the number of variants, increasing maintenance cost.

💡 Though each call is fast, this approach wastes effort and risks bugs as code repeats for each variant, making it unscalable.
Interview Verdict: Accepted but not scalable or maintainable

This approach works but is fragile and hard to extend, motivating the Template Method Pattern

🧠
Template Method Pattern - Base Class with Template Method and Abstract Steps
💡 This approach introduces the pattern by defining a base class with a fixed algorithm skeleton and abstract methods for subclasses to override, promoting reuse and controlled customization.

Intuition

Define a template method in the base class that calls abstract steps; subclasses override these steps to customize behavior without changing the algorithm flow.

Algorithm

  1. Define a base class with a template method outlining the algorithm steps.
  2. Declare abstract or placeholder methods for steps that vary.
  3. Subclasses override these methods to provide specific behavior.
  4. The template method calls all steps in order, controlling the flow.
💡 This structure separates fixed and variable parts clearly, making code easier to maintain and extend.
</>
Code
from abc import ABC, abstractmethod

class CaffeineBeverage(ABC):
    def prepare_recipe(self):
        self.boil_water()
        self.brew()
        self.pour_in_cup()
        self.add_condiments()

    def boil_water(self):
        print('Boil water')

    @abstractmethod
    def brew(self):
        pass

    def pour_in_cup(self):
        print('Pour into cup')

    @abstractmethod
    def add_condiments(self):
        pass

class Tea(CaffeineBeverage):
    def brew(self):
        print('Steep tea bag')

    def add_condiments(self):
        print('Add lemon')

class Coffee(CaffeineBeverage):
    def brew(self):
        print('Brew coffee grounds')

    def add_condiments(self):
        print('Add sugar and milk')

if __name__ == '__main__':
    print('Making Tea:')
    Tea().prepare_recipe()
    print()
    print('Making Coffee:')
    Coffee().prepare_recipe()
Line Notes
from abc import ABC, abstractmethodImports to define abstract base class and methods enforcing subclass overrides
class CaffeineBeverage(ABC):Defines abstract base class representing algorithm skeleton
def prepare_recipe(self):Template method controlling the fixed sequence of steps
def brew(self):Abstract method forcing subclasses to implement brewing step
abstract class CaffeineBeverage {
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    void boilWater() {
        System.out.println("Boil water");
    }

    abstract void brew();

    void pourInCup() {
        System.out.println("Pour into cup");
    }

    abstract void addCondiments();
}

class Tea extends CaffeineBeverage {
    void brew() {
        System.out.println("Steep tea bag");
    }

    void addCondiments() {
        System.out.println("Add lemon");
    }
}

class Coffee extends CaffeineBeverage {
    void brew() {
        System.out.println("Brew coffee grounds");
    }

    void addCondiments() {
        System.out.println("Add sugar and milk");
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("Making Tea:");
        new Tea().prepareRecipe();
        System.out.println();
        System.out.println("Making Coffee:");
        new Coffee().prepareRecipe();
    }
}
Line Notes
abstract class CaffeineBeverage {Base class defines algorithm skeleton with abstract steps
final void prepareRecipe() {Template method is final to prevent subclasses from altering algorithm flow
abstract void brew();Abstract method for subclass to implement brewing step
class Tea extends CaffeineBeverage {Subclass overrides variable steps to customize behavior
#include <iostream>
using namespace std;

class CaffeineBeverage {
public:
    void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    void boilWater() {
        cout << "Boil water" << endl;
    }

    virtual void brew() = 0;

    void pourInCup() {
        cout << "Pour into cup" << endl;
    }

    virtual void addCondiments() = 0;
    virtual ~CaffeineBeverage() {}
};

class Tea : public CaffeineBeverage {
public:
    void brew() override {
        cout << "Steep tea bag" << endl;
    }

    void addCondiments() override {
        cout << "Add lemon" << endl;
    }
};

class Coffee : public CaffeineBeverage {
public:
    void brew() override {
        cout << "Brew coffee grounds" << endl;
    }

    void addCondiments() override {
        cout << "Add sugar and milk" << endl;
    }
};

int main() {
    cout << "Making Tea:" << endl;
    Tea tea;
    tea.prepareRecipe();
    cout << endl;
    cout << "Making Coffee:" << endl;
    Coffee coffee;
    coffee.prepareRecipe();
    return 0;
}
Line Notes
class CaffeineBeverage {Abstract base class defining algorithm skeleton with pure virtual methods
void prepareRecipe() {Template method controlling fixed sequence of steps
virtual void brew() = 0;Pure virtual method forcing subclass implementation
class Tea : public CaffeineBeverage {Subclass implements variable steps to customize behavior
class CaffeineBeverage {
    prepareRecipe() {
        this.boilWater();
        this.brew();
        this.pourInCup();
        this.addCondiments();
    }

    boilWater() {
        console.log('Boil water');
    }

    brew() {
        throw new Error('Subclass must implement brew()');
    }

    pourInCup() {
        console.log('Pour into cup');
    }

    addCondiments() {
        throw new Error('Subclass must implement addCondiments()');
    }
}

class Tea extends CaffeineBeverage {
    brew() {
        console.log('Steep tea bag');
    }

    addCondiments() {
        console.log('Add lemon');
    }
}

class Coffee extends CaffeineBeverage {
    brew() {
        console.log('Brew coffee grounds');
    }

    addCondiments() {
        console.log('Add sugar and milk');
    }
}

console.log('Making Tea:');
new Tea().prepareRecipe();
console.log();
console.log('Making Coffee:');
new Coffee().prepareRecipe();
Line Notes
class CaffeineBeverage {Base class defines algorithm skeleton with methods to be overridden
prepareRecipe() {Template method calls all steps in fixed order
brew() { throw new ErrorAbstract method forcing subclass to implement brewing step
class Tea extends CaffeineBeverage {Subclass customizes specific steps without altering flow
Complexity
TimeO(1) per call
SpaceO(1) per call plus class overhead

The template method executes a fixed number of steps each time, so runtime is constant. The pattern improves code maintainability and extensibility but does not affect runtime complexity.

💡 This pattern doesn't optimize speed but greatly improves code organization and extensibility by separating fixed and variable parts.
Interview Verdict: Accepted and recommended for maintainable code

This is the canonical implementation of the Template Method Pattern and is what interviewers expect.

🧠
Template Method Pattern with Hook Method for Optional Steps
💡 This approach extends the pattern by adding a hook method that subclasses can override to optionally skip or add behavior, increasing flexibility without breaking the algorithm skeleton.

Intuition

Add a hook method returning a boolean to decide whether to execute an optional step, letting subclasses control optional behavior without overriding the template method.

Algorithm

  1. Define a hook method in the base class returning a default value.
  2. In the template method, conditionally execute optional steps based on the hook.
  3. Subclasses override the hook to enable or disable optional steps.
  4. Subclasses override abstract steps as before.
💡 Hooks provide controlled extension points without forcing subclasses to override the entire template method.
</>
Code
from abc import ABC, abstractmethod

class CaffeineBeverage(ABC):
    def prepare_recipe(self):
        self.boil_water()
        self.brew()
        self.pour_in_cup()
        if self.customer_wants_condiments():
            self.add_condiments()

    def boil_water(self):
        print('Boil water')

    @abstractmethod
    def brew(self):
        pass

    def pour_in_cup(self):
        print('Pour into cup')

    @abstractmethod
    def add_condiments(self):
        pass

    def customer_wants_condiments(self):
        return True  # Hook method with default implementation

class Tea(CaffeineBeverage):
    def brew(self):
        print('Steep tea bag')

    def add_condiments(self):
        print('Add lemon')

    def customer_wants_condiments(self):
        return False  # Tea without condiments

class Coffee(CaffeineBeverage):
    def brew(self):
        print('Brew coffee grounds')

    def add_condiments(self):
        print('Add sugar and milk')

if __name__ == '__main__':
    print('Making Tea:')
    Tea().prepare_recipe()
    print()
    print('Making Coffee:')
    Coffee().prepare_recipe()
Line Notes
def prepare_recipe(self):Template method with conditional optional step controlled by hook
if self.customer_wants_condiments():Hook method determines if optional step runs
def customer_wants_condiments(self):Hook method with default implementation returning True
def customer_wants_condiments(self): return FalseSubclass overrides hook to skip optional condiments step
abstract class CaffeineBeverage {
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    void boilWater() {
        System.out.println("Boil water");
    }

    abstract void brew();

    void pourInCup() {
        System.out.println("Pour into cup");
    }

    abstract void addCondiments();

    boolean customerWantsCondiments() {
        return true; // Hook method
    }
}

class Tea extends CaffeineBeverage {
    void brew() {
        System.out.println("Steep tea bag");
    }

    void addCondiments() {
        System.out.println("Add lemon");
    }

    @Override
    boolean customerWantsCondiments() {
        return false; // Override hook to skip condiments
    }
}

class Coffee extends CaffeineBeverage {
    void brew() {
        System.out.println("Brew coffee grounds");
    }

    void addCondiments() {
        System.out.println("Add sugar and milk");
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("Making Tea:");
        new Tea().prepareRecipe();
        System.out.println();
        System.out.println("Making Coffee:");
        new Coffee().prepareRecipe();
    }
}
Line Notes
final void prepareRecipe() {Template method with conditional hook call to control optional step
if (customerWantsCondiments()) {Hook method controls whether optional step executes
boolean customerWantsCondiments() {Hook method with default implementation returning true
boolean customerWantsCondiments() { return false; }Subclass overrides hook to disable optional step
#include <iostream>
using namespace std;

class CaffeineBeverage {
public:
    void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    void boilWater() {
        cout << "Boil water" << endl;
    }

    virtual void brew() = 0;

    void pourInCup() {
        cout << "Pour into cup" << endl;
    }

    virtual void addCondiments() = 0;

    virtual bool customerWantsCondiments() {
        return true; // Hook method
    }

    virtual ~CaffeineBeverage() {}
};

class Tea : public CaffeineBeverage {
public:
    void brew() override {
        cout << "Steep tea bag" << endl;
    }

    void addCondiments() override {
        cout << "Add lemon" << endl;
    }

    bool customerWantsCondiments() override {
        return false; // Skip condiments
    }
};

class Coffee : public CaffeineBeverage {
public:
    void brew() override {
        cout << "Brew coffee grounds" << endl;
    }

    void addCondiments() override {
        cout << "Add sugar and milk" << endl;
    }
};

int main() {
    cout << "Making Tea:" << endl;
    Tea tea;
    tea.prepareRecipe();
    cout << endl;
    cout << "Making Coffee:" << endl;
    Coffee coffee;
    coffee.prepareRecipe();
    return 0;
}
Line Notes
void prepareRecipe() {Template method with conditional hook call controlling optional step
if (customerWantsCondiments()) {Hook method determines if optional step runs
virtual bool customerWantsCondiments() {Hook method with default implementation returning true
bool customerWantsCondiments() override { return false; }Subclass disables optional step by overriding hook
class CaffeineBeverage {
    prepareRecipe() {
        this.boilWater();
        this.brew();
        this.pourInCup();
        if (this.customerWantsCondiments()) {
            this.addCondiments();
        }
    }

    boilWater() {
        console.log('Boil water');
    }

    brew() {
        throw new Error('Subclass must implement brew()');
    }

    pourInCup() {
        console.log('Pour into cup');
    }

    addCondiments() {
        throw new Error('Subclass must implement addCondiments()');
    }

    customerWantsCondiments() {
        return true; // Hook method
    }
}

class Tea extends CaffeineBeverage {
    brew() {
        console.log('Steep tea bag');
    }

    addCondiments() {
        console.log('Add lemon');
    }

    customerWantsCondiments() {
        return false; // Skip condiments
    }
}

class Coffee extends CaffeineBeverage {
    brew() {
        console.log('Brew coffee grounds');
    }

    addCondiments() {
        console.log('Add sugar and milk');
    }
}

console.log('Making Tea:');
new Tea().prepareRecipe();
console.log();
console.log('Making Coffee:');
new Coffee().prepareRecipe();
Line Notes
prepareRecipe() {Template method with conditional hook call controlling optional step
if (this.customerWantsCondiments()) {Hook method controls whether optional step executes
customerWantsCondiments() { return true; }Hook method default implementation returning true
customerWantsCondiments() { return false; }Subclass disables optional step by overriding hook
Complexity
TimeO(1) per call
SpaceO(1) per call plus class overhead

Hook adds no additional runtime complexity since it only returns a boolean. The pattern improves flexibility without affecting performance.

💡 Hooks let subclasses customize optional behavior safely without risking the integrity of the algorithm's structure.
Interview Verdict: Accepted and best practice for optional steps

Using hooks is a hallmark of a mature Template Method implementation, showing deep understanding.

📊
All Approaches - One-Glance Tradeoffs
💡 In interviews, always implement the Template Method Pattern with abstract steps and hooks (Approach 2 or 3). The naive approach is useful only to explain why the pattern is needed.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute ForceO(1) per callO(1)NoN/AMention only - never code
2. Template Method PatternO(1) per callO(1)NoN/ACode this in 95% of interviews
3. Template Method with HookO(1) per callO(1)NoN/ACode this to show advanced understanding
💼
Interview Strategy
💡 Use this guide to understand the problem deeply before interviews. Start by clarifying requirements, then explain the naive approach to show its flaws. Present the Template Method Pattern as a solution, and finally discuss hooks for optional behavior. Practice coding and testing each approach.

How to Present

Clarify the problem and confirm understanding of algorithm steps.Describe the naive approach and its drawbacks (code duplication, fragile flow).Introduce the Template Method Pattern and explain the base class with template method.Show how subclasses override specific steps without changing the algorithm.Explain hooks for optional steps and their benefits.Write code for the pattern and test with examples.

Time Allocation

Clarify: 3min → Approach explanation: 5min → Code: 10min → Testing & discussion: 7min. Total ~25min

What the Interviewer Tests

Interviewers check if you understand separation of concerns, inheritance, method overriding, and how to enforce algorithm structure while allowing flexibility. They also test your ability to explain design tradeoffs and implement hooks.

Common Follow-ups

  • How would you implement this pattern without inheritance? → Use composition and callbacks.
  • Can the template method be overridden? → Usually no, to preserve algorithm integrity.
  • How to add multiple optional steps? → Use multiple hooks or default implementations.
  • What are the downsides of this pattern? → Inflexibility if algorithm changes, subclass explosion.
💡 These follow-ups test your deeper understanding of pattern flexibility, alternatives, and limitations.
🔍
Pattern Recognition

When to Use

1) You have an algorithm with fixed steps; 2) Some steps vary by subclass; 3) You want to avoid code duplication; 4) You want to control algorithm flow centrally.

Signature Phrases

'Define skeleton of an algorithm in base class''Subclasses override specific steps''Hook method to control optional behavior'

NOT This Pattern When

Do not confuse with Strategy Pattern which delegates entire algorithm, or Observer Pattern which is about event notification.

Similar Problems

Strategy Pattern - encapsulates interchangeable algorithms but does not fix sequenceFactory Method Pattern - controls object creation, not algorithm flowState Pattern - changes behavior based on state, not fixed algorithm

Practice

(1/5)
1. Given the following code snippet, what will be printed when executing the last two lines?
from abc import ABC, abstractmethod

class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount):
        pass

class CreditCardStrategy(PaymentStrategy):
    def pay(self, amount):
        print(f"Processing credit card payment of ${amount}")

class UPIStrategy(PaymentStrategy):
    def pay(self, amount):
        print(f"Processing UPI payment of ${amount}")

class PaymentStrategyFactory:
    @staticmethod
    def get_strategy(method):
        if method == 'CreditCard':
            return CreditCardStrategy()
        elif method == 'UPI':
            return UPIStrategy()
        else:
            raise ValueError('Invalid payment method')

class PaymentProcessor:
    def __init__(self, strategy: PaymentStrategy):
        self.strategy = strategy

    def pay(self, amount):
        self.strategy.pay(amount)

processor = PaymentProcessor(PaymentStrategyFactory.get_strategy('UPI'))
processor.pay(100)
easy
A. Raises ValueError: Invalid payment method
B. Processing credit card payment of $100
C. Processing net banking payment of $100
D. Processing UPI payment of $100

Solution

  1. Step 1: Trace strategy selection

    The factory method get_strategy('UPI') returns an instance of UPIStrategy.
  2. Step 2: Trace payment method call

    The pay method of UPIStrategy prints "Processing UPI payment of $100".
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Correct strategy instance leads to correct output [OK]
Hint: Factory returns correct strategy instance for method [OK]
Common Mistakes:
  • Confusing strategy returned or output string
2. Examine the following buggy CompositeIterator code snippet. Which line contains the subtle bug that causes incorrect traversal order?
medium
A. Line initializing self.stack without reversing children.
B. Line checking hasNext() before popping from stack.
C. Line popping component from stack.
D. Line returning the component after processing.

Solution

  1. Step 1: Identify stack initialization issue

    Stack is initialized with children in original order, not reversed, causing traversal order reversal.
  2. Step 2: Confirm impact on traversal order

    Without reversing, popping from stack yields children in reverse order, breaking expected traversal.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Reversing children on stack initialization fixes traversal order [OK]
Hint: Stack must reverse children to preserve traversal order [OK]
Common Mistakes:
  • Forgetting to reverse children on stack push
  • Misplacing hasNext() check
3. Which of the following statements about the Liskov Substitution Principle is INCORRECT?
medium
A. A subclass can strengthen preconditions of an inherited method to ensure better input validation.
B. A subclass must not weaken postconditions of an inherited method.
C. Covariance in return types is allowed under LSP.
D. Contravariance in method parameter types is allowed under LSP.

Solution

  1. Step 1: Recall LSP precondition rule

    Subclasses must not strengthen preconditions; they can only maintain or weaken them.
  2. Step 2: Analyze each statement

    A subclass can strengthen preconditions of an inherited method to ensure better input validation. is incorrect because strengthening preconditions breaks substitutability. A subclass must not weaken postconditions of an inherited method. is correct; subclasses can weaken postconditions. Covariance in return types is allowed under LSP. is correct; covariance in return types is allowed. Contravariance in method parameter types is allowed under LSP. is correct; contravariance in parameter types is allowed.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Strengthening preconditions violates LSP.
Hint: Preconditions can only be weakened, not strengthened, in subclasses.
Common Mistakes:
  • Confusing precondition and postcondition rules
  • Believing strengthening preconditions is safe
  • Misunderstanding covariance and contravariance
4. Which of the following statements about the Open/Closed Principle is INCORRECT?
medium
A. OCP means you should never modify existing code once it's written
B. OCP encourages designing modules that can be extended without changing their source code
C. Abstraction and polymorphism are key enablers of OCP
D. OCP helps reduce bugs by minimizing changes to tested code

Solution

  1. Step 1: Analyze statement A

    OCP does not forbid all modifications; it encourages minimizing changes to stable, tested code but allows modifications when necessary.
  2. Step 2: Validate other statements

    Statements B, C, and D correctly describe OCP's goals and mechanisms.
  3. Step 3: Why A is incorrect

    Absolute prohibition of modification is impractical; OCP is about minimizing and isolating changes.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    OCP is about minimizing, not forbidding, modifications.
Hint: OCP minimizes, but does not forbid, code changes [OK]
Common Mistakes:
  • Interpreting OCP as no code changes ever allowed
  • Ignoring the role of abstraction in OCP
  • Underestimating OCP's impact on bug reduction
5. If the Composite pattern iterator is extended to allow reusing leaf components multiple times during traversal (e.g., shared leaves), which modification is necessary to ensure correct iteration without infinite loops?
hard
A. No change needed; the existing iterator handles reuse naturally.
B. Modify the iterator to push children in original order instead of reversed order.
C. Add a visited set to track and skip already visited components during iteration.
D. Convert the iterator to a recursive traversal to handle reuse correctly.

Solution

  1. Step 1: Understand reuse implications

    Reusing leaves means the same component can appear multiple times, risking infinite loops.
  2. Step 2: Identify solution to prevent infinite loops

    Tracking visited components prevents revisiting the same node repeatedly during iteration.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Visited set avoids infinite loops with shared components [OK]
Hint: Track visited nodes to handle shared components safely [OK]
Common Mistakes:
  • Assuming no changes needed
  • Changing push order does not fix reuse loops