What if adding a new payment method didn't mean rewriting your whole checkout system?
Why Payment strategy pattern in LLD? - Purpose & Use Cases
Start learning this pattern below
Jump into concepts and practice - no test required
Imagine you run an online store where customers can pay by credit card, PayPal, or gift card. You write separate code blocks for each payment type everywhere in your app.
Every time you add a new payment method, you must find and change code in many places.
This manual way is slow and risky. You might forget to update some places, causing bugs or crashes.
It's hard to test and maintain because payment logic is scattered and tangled.
The Payment strategy pattern lets you wrap each payment method in its own class with a common interface.
Your app just calls the interface, and the right payment method runs behind the scenes.
This keeps code clean, easy to add new methods, and safe to change.
if (type == 'card') { processCard(); } else if (type == 'paypal') { processPaypal(); } else if (type == 'gift') { processGift(); }
payment = getPaymentStrategy(type); payment.pay();
You can add or change payment methods without touching the main code, making your system flexible and robust.
Big e-commerce sites use this pattern to support many payment options worldwide without rewriting core checkout code.
Manual payment handling is error-prone and hard to maintain.
Payment strategy pattern organizes payment methods into separate, interchangeable classes.
This leads to cleaner, scalable, and easier-to-update payment systems.
Practice
Payment Strategy Pattern in a payment system?Solution
Step 1: Understand the purpose of the Payment Strategy Pattern
The pattern is designed to let the system switch payment methods easily without modifying the main logic.Step 2: Analyze the options
Only It allows switching between different payment methods without changing the main code. describes this benefit correctly. Other options describe unrelated features.Final Answer:
It allows switching between different payment methods without changing the main code. -> Option AQuick Check:
Payment Strategy Pattern = Switch payment methods easily [OK]
- Confusing strategy pattern with data storage or encryption
- Thinking it enforces currency or database rules
- Assuming it handles security automatically
Solution
Step 1: Identify the correct syntax for an interface
In object-oriented languages, interfaces declare method signatures without implementation. interface PaymentStrategy { void pay(double amount); } uses 'interface' and a method signature correctly.Step 2: Check other options
class PaymentStrategy { void pay(amount); } is a class, not an interface. Options C and D use function syntax, not interface definitions.Final Answer:
interface PaymentStrategy { void pay(double amount); } -> Option AQuick Check:
Interface syntax = interface PaymentStrategy { void pay(double amount); } [OK]
- Using class instead of interface for strategy definition
- Confusing function syntax with interface
- Missing method parameter types
class PaymentStrategy {
pay(amount) { throw 'Not implemented'; }
}
class CreditCardPayment extends PaymentStrategy {
pay(amount) { return `Paid ${amount} with Credit Card`; }
}
class PayPalPayment extends PaymentStrategy {
pay(amount) { return `Paid ${amount} with PayPal`; }
}
class PaymentContext {
constructor(strategy) { this.strategy = strategy; }
executePayment(amount) { return this.strategy.pay(amount); }
}
const context = new PaymentContext(new PayPalPayment());
console.log(context.executePayment(100));Solution
Step 1: Trace the object creation and method calls
The PaymentContext is created with a PayPalPayment strategy. Calling executePayment(100) calls PayPalPayment's pay method.Step 2: Understand the pay method output
PayPalPayment's pay returns 'Paid 100 with PayPal'. This string is printed.Final Answer:
Paid 100 with PayPal -> Option CQuick Check:
Context uses PayPalPayment = Output with PayPal [OK]
- Assuming default or CreditCardPayment is used
- Expecting an error from base class
- Confusing method override behavior
class PaymentStrategy {
pay(amount) { console.log('Paying ' + amount); }
}
class BitcoinPayment extends PaymentStrategy {
pay() { console.log('Paying with Bitcoin'); }
}
const payment = new BitcoinPayment();
payment.pay(50);Solution
Step 1: Compare method signatures in base and subclass
PaymentStrategy's pay expects an amount parameter, but BitcoinPayment's pay method does not accept any parameters.Step 2: Understand the impact of signature mismatch
Calling payment.pay(50) passes an argument, but BitcoinPayment's pay ignores it, causing unexpected behavior or errors.Final Answer:
BitcoinPayment's pay method does not accept the amount parameter. -> Option BQuick Check:
Method signature mismatch = BitcoinPayment's pay method does not accept the amount parameter. [OK]
- Thinking base class should not implement pay
- Assuming inheritance is wrong
- Confusing runtime error with syntax error
Solution
Step 1: Understand the open/closed principle in design
The system should be open for extension but closed for modification. Adding a new payment method should not require changing existing classes.Step 2: Evaluate each option
Create a new class implementing the PaymentStrategy interface for cryptocurrency and pass it to the payment context. creates a new class implementing the interface, fitting the pattern and minimizing changes. Options B and C modify existing classes, violating the principle. Use a global variable to switch payment methods inside the main payment function. uses a global variable, which is poor design.Final Answer:
Create a new class implementing the PaymentStrategy interface for cryptocurrency and pass it to the payment context. -> Option DQuick Check:
New class for new method = Create a new class implementing the PaymentStrategy interface for cryptocurrency and pass it to the payment context. [OK]
- Modifying existing payment classes
- Adding logic inside context class
- Using global variables for strategy switching
