Fundamentals 26 min read

Understanding the Strategy Pattern with Java Examples

This article explains the Strategy design pattern, demonstrates its use through a quote‑management example and various Java implementations, discusses the open‑closed principle, shows how to apply the pattern to different scenarios such as payment processing and thread‑pool rejection handling, and outlines its advantages and drawbacks.

Java Captain
Java Captain
Java Captain
Understanding the Strategy Pattern with Java Examples

The article introduces the Strategy pattern by first presenting a real‑world scenario of offering different discounts to new, existing, and VIP customers in a shopping mall.

It shows an initial naive implementation where all discount calculations are placed inside a single quote method, leading to a bulky and hard‑to‑maintain class.

package strategy.examp02;

import java.math.BigDecimal;

public class QuoteManager {
    public BigDecimal quote(BigDecimal originalPrice, String customType) {
        if ("新客户".equals(customType)) {
            System.out.println("抱歉!新客户没有折扣!");
            return originalPrice;
        } else if ("老客户".equals(customType)) {
            System.out.println("恭喜你!老客户打9折!");
            originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2, BigDecimal.ROUND_HALF_UP);
            return originalPrice;
        } else if ("VIP客户".equals(customType)) {
            System.out.println("恭喜你!VIP客户打8折!");
            originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2, BigDecimal.ROUND_HALF_UP);
            return originalPrice;
        }
        //其他人员都是原价
        return originalPrice;
    }
}

After testing, the code works but violates the Open‑Closed Principle because adding a new customer type requires modifying the existing method.

The improved version extracts each discount algorithm into its own method, reducing the size of quote but still requiring changes to the method when new types appear.

package strategy.examp02;

import java.math.BigDecimal;

public class QuoteManagerImprove {
    public BigDecimal quote(BigDecimal originalPrice, String customType) {
        if ("新客户".equals(customType)) {
            return this.quoteNewCustomer(originalPrice);
        } else if ("老客户".equals(customType)) {
            return this.quoteOldCustomer(originalPrice);
        } else if ("VIP客户".equals(customType)) {
            return this.quoteVIPCustomer(originalPrice);
        }
        //其他人员都是原价
        return originalPrice;
    }

    private BigDecimal quoteVIPCustomer(BigDecimal originalPrice) {
        System.out.println("恭喜!VIP客户打8折");
        originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2, BigDecimal.ROUND_HALF_UP);
        return originalPrice;
    }

    private BigDecimal quoteOldCustomer(BigDecimal originalPrice) {
        System.out.println("恭喜!老客户打9折");
        originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2, BigDecimal.ROUND_HALF_UP);
        return originalPrice;
    }

    private BigDecimal quoteNewCustomer(BigDecimal originalPrice) {
        System.out.println("抱歉!新客户没有折扣!");
        return originalPrice;
    }
}

To achieve true extensibility, the article applies the Strategy pattern: a common interface IQuoteStrategy defines BigDecimal getPrice(BigDecimal originalPrice) , and each customer type gets its own concrete strategy class.

package strategy.examp02;

public interface IQuoteStrategy {
    BigDecimal getPrice(BigDecimal originalPrice);
}

Concrete strategies for new, old, VIP, and MVP customers implement this interface, each containing only the logic relevant to its type.

package strategy.examp02;

public class NewCustomerQuoteStrategy implements IQuoteStrategy {
    @Override
    public BigDecimal getPrice(BigDecimal originalPrice) {
        System.out.println("抱歉!新客户没有折扣!");
        return originalPrice;
    }
}
package strategy.examp02;

public class OldCustomerQuoteStrategy implements IQuoteStrategy {
    @Override
    public BigDecimal getPrice(BigDecimal originalPrice) {
        System.out.println("恭喜!老客户享有9折优惠!");
        return originalPrice.multiply(new BigDecimal(0.9)).setScale(2, BigDecimal.ROUND_HALF_UP);
    }
}
package strategy.examp02;

public class VIPCustomerQuoteStrategy implements IQuoteStrategy {
    @Override
    public BigDecimal getPrice(BigDecimal originalPrice) {
        System.out.println("恭喜!VIP客户享有8折优惠!");
        return originalPrice.multiply(new BigDecimal(0.8)).setScale(2, BigDecimal.ROUND_HALF_UP);
    }
}
package strategy.examp02;

public class MVPCustomerQuoteStrategy implements IQuoteStrategy {
    @Override
    public BigDecimal getPrice(BigDecimal originalPrice) {
        System.out.println("哇偶!MVP客户享受7折优惠!!!");
        return originalPrice.multiply(new BigDecimal(0.7)).setScale(2, BigDecimal.ROUND_HALF_UP);
    }
}

A QuoteContext holds a reference to an IQuoteStrategy and delegates the price calculation to it.

package strategy.examp02;

public class QuoteContext {
    private IQuoteStrategy quoteStrategy;

    public QuoteContext(IQuoteStrategy quoteStrategy) {
        this.quoteStrategy = quoteStrategy;
    }

    public BigDecimal getPrice(BigDecimal originalPrice) {
        return quoteStrategy.getPrice(originalPrice);
    }
}

Clients create the desired strategy, inject it into the context, and obtain the discounted price without touching existing code.

package strategy.examp02;

public class Client {
    public static void main(String[] args) {
        IQuoteStrategy oldQuoteStrategy = new OldCustomerQuoteStrategy();
        QuoteContext quoteContext = new QuoteContext(oldQuoteStrategy);
        BigDecimal price = quoteContext.getPrice(new BigDecimal(100));
        System.out.println("折扣价为:" + price);
    }
}

The article then explores more sophisticated examples: a historical analogy with Liu Bei’s three plans, a payment‑processing system where strategies (RMB, Dollar, Account) are selected at runtime, and the JDK’s RejectedExecutionHandler hierarchy used by ThreadPoolExecutor . Each example demonstrates how the pattern separates algorithmic variation from the stable context, adheres to the Open‑Closed Principle, and enables easy addition of new strategies such as MVP discounts or bank‑account payments.

Finally, the advantages (clear separation of algorithms, replaceability, better extensibility) and disadvantages (client must know all strategies, increased number of classes, suitability only for flat algorithm structures) of the Strategy pattern are listed, concluding that its essence is “separate algorithm, choose implementation”.

design patternsJavaStrategy PatternOOPopen-closed principle
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.