Definition
Enum-based Strategy pattern:
- Abstract method on enum: Each constant must override it with its own behavior.
- Each constant is a strategy:
SAVINGS.calculateInterest(...)uses a different formula thanLOAN.calculateInterest(...). - Closed set: Only the defined constants can provide strategies. No external code can add one.
- Type-safe selection: Pass the enum constant to select the strategy. No string matching or if-else chains.
The bank has three types of interest calculation forms: one for savings, one for current, one for loans. Each form has the same header (abstract method signature) but different calculation steps (each constant's implementation). The teller picks the form matching the account type.
Understanding the Concepts
How does it work?
public enum AccountType {
SAVINGS {
@Override
public BigDecimal calculateInterest(BigDecimal balance) {
return balance.multiply(new BigDecimal("0.04")); // 4%
}
},
CURRENT {
@Override
public BigDecimal calculateInterest(BigDecimal balance) {
return BigDecimal.ZERO; // no interest
}
},
LOAN {
@Override
public BigDecimal calculateInterest(BigDecimal balance) {
return balance.multiply(new BigDecimal("0.085")); // 8.5%
}
};
// Abstract method: every constant MUST implement this
public abstract BigDecimal calculateInterest(BigDecimal balance);
}
// Usage: no if-else, no switch – polymorphic dispatch on the enum
AccountType type = AccountType.SAVINGS;
BigDecimal interest = type.calculateInterest(new BigDecimal("500000"));
System.out.println(interest); // 20000
Adding a new account type forces you to implement calculateInterest() – the compiler ensures completeness.
How is this the Strategy pattern?
The Strategy pattern says: define a family of algorithms, encapsulate each one, and make them interchangeable.
- Family of algorithms: The three interest calculation formulas.
- Encapsulated: Each constant contains its own implementation.
- Interchangeable: Pass the enum constant to select the strategy.
Without enum:
// BAD: if-else chain, not extensible, easy to forget a case
if (type.equals("SAVINGS")) { ... }
else if (type.equals("CURRENT")) { ... }
else if (type.equals("LOAN")) { ... }
With enum: the dispatch is built into the type system. No if-else needed.
When should you use enum Strategy vs interface Strategy?
| Enum Strategy | Interface Strategy |
|---|---|
| Closed set of strategies | Open set (new strategies can be added) |
| All strategies in one file | Strategies in separate classes |
| Simple, few strategies | Complex strategies with dependencies |
| No Spring injection needed | Strategies need injected dependencies |
What should a senior engineer know?
- Combine with fields and methods. Each constant can have its own data (fee rates, limits) and behavior (calculation methods).
- Use sealed interfaces (Java 17+) for open-but-controlled strategy sets:
sealed interface Strategy permits FixedRate, FloatingRate, ZeroRate {}. - In Spring, use interface-based Strategy when strategies need injected services (repository, external API). Enum constants cannot have Spring-managed dependencies.
What are common mistakes?
A common mistake is not understanding the concept fully before using it in production banking applications.
Wrong approach:
package com.bank.service;
public class TransferService {
public void transfer(String fromAccount, String toAccount, double amount) {
// WRONG: using double for money
double balance = getBalance(fromAccount);
balance = balance - amount;
}
private double getBalance(String id) { return 10000.0; }
}
Correct approach:
package com.bank.service;
import java.math.BigDecimal;
public class TransferService {
public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
// CORRECT: using BigDecimal for money
BigDecimal balance = getBalance(fromAccount);
balance = balance.subtract(amount);
}
private BigDecimal getBalance(String id) { return new BigDecimal("10000.00"); }
}
Always use BigDecimal for monetary calculations in banking systems to avoid floating-point precision errors.
What are the edge cases?
Edge cases can cause unexpected behavior in banking applications. Here are some to watch for.
package com.bank.edge;
public class EdgeCaseDemo {
public static void main(String[] args) {
// Edge case 1: null handling
String accountId = null;
try {
System.out.println(accountId.length());
} catch (NullPointerException e) {
System.out.println("Null account ID");
}
// Edge case 2: empty vs null
String emptyId = "";
System.out.println(emptyId.isEmpty()); // true
System.out.println(emptyId == null); // false
// Edge case 3: boundary values
java.math.BigDecimal zero = java.math.BigDecimal.ZERO;
java.math.BigDecimal negativeBalance = new java.math.BigDecimal("-0.01");
System.out.println(zero.compareTo(negativeBalance) > 0); // true
}
}
Null account ID
true
false
true
Always test with null values, empty strings, boundary values, and negative amounts in banking applications.
What are common interview follow-up questions?
Interviewers often follow up with these related questions:
- How does this concept apply to a high-throughput banking transaction system?
- What are the performance implications in a production environment?
- How would you debug issues related to this concept?
- What tools would you use to monitor this in production?
- How does this behave differently in Java 8 vs Java 17+?
When answering follow-up questions, always relate your answer back to practical experience. For example, at National Bank of India, Rajesh debugged a production issue related to this concept by using JFR recordings and heap dumps. Providing concrete examples from banking systems shows the interviewer that you understand both the theory and the practice.
Visual Overview
enum AccountType:
SAVINGS → calculateInterest: balance * 4%
CURRENT → calculateInterest: 0
LOAN → calculateInterest: balance * 8.5%
type.calculateInterest(500000)
│ polymorphic dispatch on the enum constant
▼
SAVINGS → 20000
CURRENT → 0
LOAN → 42500
No if-else, no switch – behavior is built into the type.
Key Takeaway
An enum with an abstract method implements the Strategy pattern by forcing each constant to provide its own implementation. The behavior varies by constant and is selected at runtime through polymorphic dispatch, with no if-else or switch needed.
This is ideal for small, closed sets of strategies like account-type-specific interest calculations. Adding a new constant automatically requires implementing the abstract method, so the compiler ensures completeness.
One sentence to remember: declare an abstract method on the enum and each constant becomes a type-safe strategy with its own implementation.
---