Bird
Raised Fist0

Template Method Pattern - Defining Algorithm Skeleton, Subclasses Fill In Steps

LLDeasyInterview freq: high
Choose your preparation mode4 modes available
🏗️
Template Method Pattern - Defining Algorithm Skeleton, Subclasses Fill In Steps
Define the skeleton of an algorithm in an abstract class, deferring some steps to subclasses.
easyDesign Patterns - AppliedFreq: high
Prerequisites:OOP BasicsAbstract Classes and InheritanceMethod Overriding in Java
⚙️
Functional Requirements
  • Define an abstract class with a final template method representing the algorithm skeleton.
  • Template method calls a sequence of concrete and abstract methods representing algorithm steps.
  • Subclasses must implement abstract steps to provide specific behavior.
  • Allow optional hooks with default empty implementations in the abstract class.
  • Ensure subclasses cannot override the template method to maintain algorithm integrity.
  • Support multiple subclasses with different implementations of the steps.
  • Provide clear extension points for subclasses to customize behavior.
👤
Use Cases
  • Client invokes a high-level algorithm method defined in an abstract class.
  • Subclasses override specific steps of the algorithm without changing the overall structure.
  • Ensure algorithm steps execute in a fixed order defined by the abstract class.
  • Allow subclasses to optionally override hooks to customize behavior.
  • Prevent clients from altering the algorithm's skeleton.
🧑‍💻
Actors
  • Client
  • AbstractAlgorithm
  • ConcreteAlgorithmSubclass
❓
Clarifying Questions to Ask
  • Will multiple threads invoke the template method concurrently on the same instance?
  • Should the algorithm steps maintain any shared mutable state?
  • Are there any performance constraints on the algorithm execution?
  • Is it acceptable for subclasses to override the template method itself?
  • Should the algorithm support conditional execution of steps based on runtime data?
  • Are there any error handling or rollback requirements within the algorithm steps?
Out of Scope
✗ Concurrency control for multiple clients invoking the algorithm simultaneously.
✗ Dynamic modification of algorithm steps at runtime.
✗ Alternative patterns like Strategy or State.
⚡
QPS Calculation
write qps100
read qps1000
🖥️
Server Estimate
3
Estimates based on typical usage of algorithm invocations in concurrent environments.
🔢
Enumerations
enum AlgorithmStepStatus
NOT_STARTED | IN_PROGRESS | COMPLETED | FAILED
Represents the execution state of each algorithm step to track progress and handle errors explicitly.
enum HookType
PRE_PROCESS | POST_PROCESS
Defines optional hook points in the algorithm to allow subclasses to inject behavior before or after main steps.
📦
AbstractAlgorithm
2 fields
private volatile AlgorithmStepStatus statusprivate final Object lock
Defines the template method as final to enforce algorithm skeleton. Declares abstract steps for subclasses to implement. Provides optional hooks with default empty implementations. Manages step execution status with volatile to ensure visibility across threads.
Why thisAbstract class allows partial method implementations and instance state, which interfaces in Java prior to default methods cannot provide.
Why notInterfaces lack ability to hold state and provide partial implementations, making them unsuitable for defining algorithm skeletons with shared code.
public final void templateMethod()Defines the fixed sequence of algorithm steps, invoking concrete and abstract methods in order.
protected abstract void stepOne()Abstract method representing the first customizable step of the algorithm.
protected abstract void stepTwo()Abstract method representing the second customizable step of the algorithm.
protected void hook(HookType type)Optional hook method with empty default implementation, subclasses may override to add behavior.
public AlgorithmStepStatus getStatus()Returns the current execution status of the algorithm steps.
📦
ConcreteAlgorithmA
1 fields
private String data
Implements abstract steps with specific behavior. Optionally overrides hooks to customize pre/post processing.
Why thisSubclass provides specific algorithm steps implementation to customize behavior.
Why notAvoids duplicating algorithm skeleton code by reusing abstract class template method.
protected void stepOne()Concrete implementation of step one.
protected void stepTwo()Concrete implementation of step two.
protected void hook(HookType type)Overrides hook to add custom behavior before or after steps.
📦
ConcreteAlgorithmB
1 fields
private int counter
Alternative subclass providing different implementations of abstract steps.
Why thisProvides alternative behavior for algorithm steps to support different use cases.
Why notReuses template method from abstract class to maintain fixed algorithm structure.
protected void stepOne()Concrete implementation of step one with different logic.
protected void stepTwo()Concrete implementation of step two with different logic.
📦
Client
0 fields
Uses AbstractAlgorithm reference to invoke the template method polymorphically.
Why thisClient interacts with algorithm via abstract interface to support polymorphism.
Why notDirectly invoking concrete classes would reduce flexibility and extensibility.
public void executeAlgorithm(AbstractAlgorithm algorithm)Calls the templateMethod on the passed AbstractAlgorithm instance.
🔗
Relationships
ConcreteAlgorithmA──►AbstractAlgorithm[1]
inheritancesubclass lifecycle depends on abstract class
ConcreteAlgorithmB──►AbstractAlgorithm[1]
inheritancesubclass lifecycle depends on abstract class
Client──►AbstractAlgorithm[1]
associationClient uses AbstractAlgorithm instance but does not own it
🗄️
AlgorithmExecution
PK: executionId
executionId UUIDstepOrder intstepName textstatus texttimestamp timestamp
Stores execution status of each step in the algorithm for tracking and auditing.
🗄️
AlgorithmConfig
PK: algorithmName
algorithmName textversion intconfigData text
Stores configuration data for different algorithm versions.
🔌
POST
/executeAlgorithm
Request
{
  "algorithmType": "string",
  "inputData": "object"
}
Response
{
  "executionId": "UUID",
  "status": "string"
}
Starts execution of the specified algorithm type with given input.
🔌
GET
/getExecutionStatus
Request
{
  "executionId": "UUID"
}
Response
{
  "executionId": "UUID",
  "stepStatuses": "list of step status objects"
}
Retrieves current status of algorithm execution steps.
🔌
GET
/listAlgorithms
Request
{}
Response
{
  "algorithms": "list of available algorithm types and versions"
}
Lists all supported algorithm implementations.

Practice

(1/5)
1. A logging framework has a Logger class with a private constructor and a static synchronized getInstance() method returning the single instance. Which design pattern is applied here and why is it the correct choice?
easy
A. Factory pattern, because it controls object creation centrally.
B. Prototype pattern, to clone Logger instances efficiently.
C. Builder pattern, to construct Logger with complex configurations.
D. Singleton pattern, ensuring only one Logger instance exists globally.

Solution

  1. Step 1: Identify the pattern from private constructor and static getInstance()

    The private constructor prevents external instantiation, and the static synchronized getInstance() ensures a single shared instance, which matches the Singleton pattern.
  2. Step 2: Why Singleton is correct here

    Logging frameworks require a single global Logger instance to coordinate log output consistently. Factory, Builder, and Prototype patterns do not enforce single instance semantics.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Private constructor + static synchronized getInstance() -> Singleton [check]
Hint: Private constructor + static getInstance() -> Singleton [check]
Common Mistakes:
2. In designing a plugin interface in Java, should the Plugin be an abstract class or an interface if plugins must maintain internal state and provide default lifecycle methods? Why?
easy
A. Interface, because it allows multiple inheritance and enforces method signatures without state.
B. Interface, because Java interfaces can now have private fields for state.
C. Abstract class, because it can hold shared state and provide default method implementations.
D. Abstract class, because it forces all plugins to share the same constructor signature.

Solution

  1. Step 1: Analyze state and default lifecycle method needs

    Interfaces cannot hold instance state; abstract classes can hold fields and provide default method bodies.
  2. Step 2: Determine which construct fits stateful plugins with default behavior

    Abstract class fits because it supports shared state and default lifecycle implementations, easing plugin development.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Abstract class supports state and default methods [check]
Hint: State + default methods = abstract class, not interface [check]
Common Mistakes:
3. The auction system currently calculates fees with a fixed strategy. To add a WeekendFeeStrategy without modifying existing classes, which SOLID principle is being applied and what is the minimum required change?
medium
A. Open/Closed Principle: create a new WeekendFeeStrategy class implementing FeeStrategy; inject it without changing existing code.
B. Liskov Substitution Principle: subclass Auction to WeekendAuction overriding fee calculation.
C. Single Responsibility Principle: modify Auction class to handle weekend fees directly.
D. Dependency Inversion Principle: hardcode weekend fee logic inside FeeStrategy implementation.

Solution

  1. Step 1: Identify SOLID principle

    Open/Closed Principle (OCP) states software entities should be open for extension but closed for modification.
  2. Step 2: Apply OCP by adding WeekendFeeStrategy implementing FeeStrategy interface without modifying existing classes.

  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New strategy class + injection -> OCP compliance [check]
Hint: Add new strategy class, no existing code change -> OCP [check]
Common Mistakes:
4. In a multi-threaded environment, if the clone() method is not synchronized, which exact sequence of events can cause two threads to receive the same cloned object instance, leading to shared mutable state?
medium
A. Thread 1 calls clone(), completes; Thread 2 calls clone(), completes immediately after
B. Thread 1 starts clone(), copies partial state; context switch; Thread 2 starts clone(), copies same partial state; both return identical shared references
C. Thread 1 and Thread 2 both call clone() simultaneously but get different instances due to JVM guarantees
D. Thread 1 calls clone(), context switch; Thread 2 calls clone(), but Thread 1 finishes first, so no conflict

Solution

  1. Step 1: Identify race condition in clone()

    Without synchronization, clone() can copy partial or inconsistent state if interrupted mid-copy.
  2. Step 2: Trace exact interleaving causing shared mutable state

    Thread 1 copies partial state, context switch to Thread 2 which copies same partial state, both return references to the same underlying mutable object.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Unsynchronized clone() -> partial copy race -> shared mutable instance [check]
Hint: Unsync clone() -> partial copy race -> shared object [check]
Common Mistakes:
5. The system needs to support ElectricCar playback devices that require ChargingSpot management. What is the minimum change to the current design to support this without modifying existing classes?
hard
A. Add ELECTRIC_CAR to DeviceType enum, CHARGING to SpotType enum, create ElectricCar extends Device, and update SpotCompatibility map externally.
B. Modify existing Device class to add charging logic and add ElectricCar as a flag inside it.
C. Create a new ElectricCar class unrelated to Device and duplicate playback logic.
D. Add charging logic inside the PlaybackQueue class to handle ElectricCar specifics.

Solution

  1. Step 1: Identify extensibility without modifying existing classes

    Adding new enum values and subclassing allows extension without changing existing class code.
  2. Step 2: Update compatibility externally

    Updating SpotCompatibility map outside existing classes respects OCP and avoids class explosion or god-class logic.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Extend enums + subclass + external map update -> minimal change [check]
Hint: Extend enums + subclass + external map -> minimal change [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard