Shopping Cart Discount System - Strategy Pattern Implementation
This is an implementation of the Strategy design pattern for handling dynamic pricing and discount coupon codes in an e-commerce checkout system.
Problem Statement
Building a pricing engine for an e-commerce site where users can apply different types of coupon codes at checkout. Each discount type requires different calculation logic, but the system must remain flexible and extensible.
Class Diagram
+------------------+ +-----------------------------+
| DiscountType | <.........| StrategyFactory |
| (Enum) | +-----------------------------+
+------------------+ | + getStrategy(type, value) |
+--------------+--------------+
|
| (Creates)
v
+------------------+ +-----------------------------+
| DiscountService | | DiscountStrategy |
| (Context) | | (Interface) |
+------------------+ +-----------------------------+
| - strategy |<>-------->| + applyDiscount(amount) |
| + applyCoupon() | +--------------+--------------+
+------------------+ ^
| (Implements)
|
+-----------------------+----------+-----------+--------------------------+
| | | |
+--------------------+ +----------------------+ +----------------------------+
| NoDiscountStrategy | | FlatDiscountStrategy | | PercentageDiscountStrategy |
+--------------------+ +----------------------+ +----------------------------+
| + apply() | | - discountAmount | | - percentage |
+--------------------+ | + apply() | | + apply() |
+----------------------+ +----------------------------+
Implementation
package shoppingcartdiscountsystem;
/**
* Main class for the Shopping Cart Discount System.
* Implements Strategy Pattern to handle dynamic pricing rules.
*/
public class ShoppingCartDiscountSystem {
/**
* Enum to define the supported types of discounts.
* Ensures type safety and prevents invalid string inputs.
*/
enum DiscountType {
NO_DISCOUNT,
FLAT_DISCOUNT,
PERCENTAGE_DISCOUNT
}
/**
* Strategy Interface.
* Defines the contract that all discount algorithms must follow.
*/
interface DiscountStrategy {
/**
* Calculates the final price after applying the discount.
* @param amount The original total amount.
* @return The final discounted price.
*/
Double applyDiscount(Double amount);
}
/**
* Concrete Strategy for No Discount.
* Returns the original amount as is.
*/
static class NoDiscountStrategy implements DiscountStrategy {
@Override
public Double applyDiscount(Double amount) {
System.out.println("No Discount Applied with this coupon!");
return amount;
}
}
/**
* Concrete Strategy for Flat Amount Discount.
* Accepts a specific amount (e.g., 50, 100) via constructor.
*/
static class FlatDiscountStrategy implements DiscountStrategy {
private final double discountAmount;
/**
* Constructor to inject the flat discount value.
* @param discountAmount The amount to be subtracted (e.g., 50.0).
*/
public FlatDiscountStrategy(double discountAmount) {
this.discountAmount = discountAmount;
}
@Override
public Double applyDiscount(Double amount) {
System.out.println("Flat " + this.discountAmount +
" rs discount applied with this coupon!");
return Math.max(0, amount - this.discountAmount);
}
}
/**
* Concrete Strategy for Percentage Discount.
* Accepts a specific percentage (e.g., 10, 20) via constructor.
*/
static class PercentageDiscountStrategy implements DiscountStrategy {
private final double percentage;
/**
* Constructor to inject the percentage value.
* @param percentage The percentage to deduct (e.g., 20.0).
*/
public PercentageDiscountStrategy(double percentage) {
this.percentage = percentage;
}
@Override
public Double applyDiscount(Double amount) {
System.out.println(this.percentage + "% Discount applied with this coupon!");
return Math.max(0, amount - (amount * (this.percentage / 100)));
}
}
/**
* Factory Class.
* Handles the creation of strategies based on the Enum type and value.
*/
static class StrategyFactory {
/**
* Creates the appropriate strategy object.
* @param discountType The type of discount (Enum).
* @param value The value for the discount (Amount or Percentage).
* @return A ready-to-use DiscountStrategy object.
*/
public static DiscountStrategy getStrategy(DiscountType discountType, double value) {
switch (discountType) {
case NO_DISCOUNT -> {
return new NoDiscountStrategy();
}
case FLAT_DISCOUNT -> {
return new FlatDiscountStrategy(value);
}
case PERCENTAGE_DISCOUNT -> {
return new PercentageDiscountStrategy(value);
}
default -> {
throw new IllegalArgumentException("Unsupported Discount Type: " +
discountType);
}
}
}
}
/**
* Context Class (Discount Service).
* Manages the active strategy and delegates the calculation.
*/
static class DiscountService {
private DiscountStrategy strategy;
/**
* Sets the discount strategy dynamically at runtime.
*/
public void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
/**
* Executes the discount logic on the given amount.
*/
public void applyDiscountCoupon(Double amount) {
if (strategy == null) {
System.out.println("Error: No Discount Coupon selected");
return;
}
System.out.println("\n----Getting Discount Coupon-----");
Double finalAmount = strategy.applyDiscount(amount);
System.out.println("Final Amount After Discount: " + finalAmount);
System.out.println("----------------------------------");
}
}
/**
* Main driver method to simulate the user interaction.
*/
public static void main(String[] args) {
DiscountService amazon = new DiscountService();
System.out.println("Simulation User: Shiv");
// Scenario 1: No Discount
try {
DiscountStrategy strategy = StrategyFactory.getStrategy(
DiscountType.NO_DISCOUNT, 0.0
);
amazon.setStrategy(strategy);
amazon.applyDiscountCoupon(100.00);
} catch (Exception e) {
System.out.println(e.getMessage());
}
// Scenario 2: Flat Discount (₹50 off)
try {
DiscountStrategy strategy = StrategyFactory.getStrategy(
DiscountType.FLAT_DISCOUNT, 50
);
amazon.setStrategy(strategy);
amazon.applyDiscountCoupon(100.00);
} catch (Exception e) {
System.out.println(e.getMessage());
}
// Scenario 3: Percentage Discount (20% off)
try {
DiscountStrategy strategy = StrategyFactory.getStrategy(
DiscountType.PERCENTAGE_DISCOUNT, 20
);
amazon.setStrategy(strategy);
amazon.applyDiscountCoupon(100.00);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
Key Features
- Strategy Pattern: Encapsulates different discount calculation algorithms
- Factory Pattern: Centralizes strategy object creation
- Dynamic Values: Supports configurable discount amounts and percentages
- Type Safety: Uses Enum to prevent invalid discount types
- Extensible: Easy to add new discount types (e.g., Buy-One-Get-One, Loyalty Points)
-
Safety Check: Uses
Math.max(0, ...)to prevent negative prices
Top comments (0)