DEV Community

NOOB
NOOB

Posted on

LLD-3:Shopping Cart Discount System

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()                  |
                                 +----------------------+  +----------------------------+
Enter fullscreen mode Exit fullscreen mode

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());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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)