ArchitectMind
  1. Home
  2. Tutorials
  3. Book 5 · Software Design & Design Patterns
  4. Volume IV
  5. Strategy Pattern
Book 5 · Volume IV — Behavioral Design Patterns · Topic 1.1

Strategy Pattern

Encapsulated algorithms and runtime selection

7 min readStage 2 · Frameworks & design7 interview questions
Chapter
Chapter 1 — Strategy
Prerequisites
Volume II (OCP, DIP); Volume III Chapter 11 Topic 11.4 (Bridge vs Strategy)
You will be able to
Explain the Strategy structure, encapsulate a family of algorithms behind one interface, select strategies at runtime from configuration or data, and keep contexts free of algorithm-specific branching
Covers
Encapsulated algorithms; Runtime selection
On this page
  1. 1. Concept / Definition
  2. 2. Why It Exists
  3. 3. Core Concepts
  4. 4. How It Works Internally — Delegation and Dispatch
  5. 4A. Deep Dive — Strategies With Data, Composition and Validation
  6. 5. APIs / Features / Syntax
  7. 6. Practical Examples
  8. 7. Common Problems & Mistakes
  9. 8. Performance & Production Considerations
  10. 9. Architecture & Design Considerations
  11. 10. Interview Questions & Answers

1Concept / Definition

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. The object that uses an algorithm — the context — depends only on the strategy interface, so the algorithm can vary independently of the clients that use it.

Strategy structure (GoF)
Figure 1 — Strategy structure (GoF)
RoleResponsibilityExample
StrategyInterface declaring the algorithmShippingPolicy.cost(parcel, dest)
Concrete strategyOne implementationFlatRate, ByWeight, FreeOver50
ContextUses a strategy as part of its own workCheckout.total(cart)
Client / selectorChooses which strategy the context getsRegistry keyed by customer segment
Table 1 — Strategy roles

2Why It Exists

Business rules vary: shipping costs by country, discounts by segment, retries by failure type, sorting by user preference. Embedding every variant inside the using class produces long conditionals that grow with each new rule, mix unrelated concerns and require touching (and retesting) the same class for every change. Strategy isolates each variant, lets new variants be added without modifying existing code (Open/Closed Principle) and allows the choice to be made at runtime — per request, per tenant or per configuration.

3Core Concepts

3.1 Encapsulated algorithms

Encapsulation means each algorithm lives in its own unit with a clear contract, hidden from the context.

JAVA
public interface ShippingPolicy {                                  // Strategy
    Money cost(Parcel parcel, Destination destination);
}
 
public record FlatRate(Money amount) implements ShippingPolicy {  // Concrete strategies
    public Money cost(Parcel p, Destination d) { return amount; }
}
 
public record ByWeight(Money base, Money perKg) implements ShippingPolicy {
    public Money cost(Parcel p, Destination d) {
        return base.plus(perKg.times(p.weightKg()));
    }
}
 
public record FreeOver(Money threshold, ShippingPolicy otherwise)
        implements ShippingPolicy {
    public Money cost(Parcel p, Destination d) {
        return p.value().isAtLeast(threshold) ? Money.ZERO : otherwise.cost(p, d);
    }
}

The context never inspects which strategy it holds:

JAVA
public final class Checkout {                                      // Context
    private final ShippingPolicy shipping;
    private final TaxCalculator tax;
 
    public Checkout(ShippingPolicy shipping, TaxCalculator tax) {
        this.shipping = shipping;
        this.tax = tax;
    }
 
    public Money total(Cart cart, Destination dest) {
        Money subtotal = cart.subtotal();
        Money ship = shipping.cost(cart.parcel(), dest);   // delegate the variation
        return subtotal.plus(ship).plus(tax.on(subtotal.plus(ship), dest));
    }
}
Property of a well-encapsulated strategyWhy it matters
One responsibility (one decision)Easy to name, test and replace
Depends only on its inputs and injected collaboratorsNo hidden coupling to the context
Same contract for all implementations (LSP)Context works with any of them
Stateless or immutable parametersSafe to share across threads
Table 2 — Encapsulation checklist

Note that FreeOver composes another strategy — strategies can be combined like decorators when their contract allows it.

3.2 Runtime selection

Strategies become powerful when the choice is made from data at runtime.

Runtime selection of a strategy
Figure 2 — Runtime selection of a strategy
JAVA
@Component
public final class ShippingPolicies {
    private final Map<Segment, ShippingPolicy> bySegment;
    private final ShippingPolicy standard;
 
    public ShippingPolicies(ShippingProps props) {
        this.standard = new ByWeight(props.base(), props.perKg());
        this.bySegment = Map.of(
            Segment.GOLD,  new FreeOver(Money.eur("50"), standard),
            Segment.STAFF, new FlatRate(Money.ZERO),
            Segment.B2B,   new FlatRate(props.b2bFlat()));
    }
 
    public ShippingPolicy forCustomer(Customer c) {
        return bySegment.getOrDefault(c.segment(), standard);    // one selection point
    }
}
A call through a strategy
Figure 3 — A call through a strategy
Selection sourceExampleTypical binding
Configurationpricing.strategy=tieredAt startup
Tenant settingsBrand A uses weight-based shippingPer tenant, cached
Request dataCustomer segment, country, devicePer request
Feature flagCanary new algorithm for 5% of trafficPer request
Runtime metricsSwitch load-balancing algorithm under stressDynamic
Table 3 — Where the choice comes from

Keeping selection in one component (registry, factory, Spring bean map) means adding a strategy changes only that component and a new class — never the context.

4How It Works Internally — Delegation and Dispatch

1. The selector returns a strategy instance typed as the interface.

2. The context stores it (constructor injection) or receives it per call (method parameter).

3. When the context reaches the varying step, it calls the interface method.

4. The JVM dispatches the call to the concrete implementation — a virtual call, typically inlined by the JIT when one or two implementations dominate at a call site.

5. The result flows back; the context continues its algorithm.

Two binding styles exist:

StyleCode shapeUse when
Strategy per context instancenew Checkout(policy)Strategy fixed for the context's lifetime
Strategy per callcheckout.total(cart, policy)Strategy varies per request and context is shared
Table 4 — Binding styles

4ADeep Dive — Strategies With Data, Composition and Validation

Real strategies often carry parameters (rates, thresholds). Records make them value objects that can be loaded from configuration, compared and logged:

JAVA
public sealed interface DiscountPolicy permits NoDiscount, Percent, Tiered {
    Money discount(Cart cart);
}
public record NoDiscount() implements DiscountPolicy {
    public Money discount(Cart c) { return Money.ZERO; }
}
public record Percent(BigDecimal rate) implements DiscountPolicy {
    public Percent {
        if (rate.signum() < 0 || rate.compareTo(new BigDecimal("0.5")) > 0)
            throw new IllegalArgumentException("rate out of range: " + rate);
    }
    public Money discount(Cart c) { return c.subtotal().times(rate); }
}
public record Tiered(List<Tier> tiers) implements DiscountPolicy {
    public Money discount(Cart c) {
        return tiers.stream().filter(t -> c.subtotal().isAtLeast(t.from()))
                    .reduce((a, b) -> b).map(t -> c.subtotal().times(t.rate()))
                    .orElse(Money.ZERO);
    }
}
TechniqueBenefit
Records as strategiesParameters validated in the compact constructor; toString for logs
Sealed strategy interfaceClosed, reviewable set; exhaustive handling in admin UIs and serialisation
Composing strategies (FreeOver(threshold, otherwise))New behaviour without new classes
Default/fallback strategySelection never returns null
Table 5 — Advanced strategy techniques

Sealing the interface is a deliberate choice: it prevents external extension (good for business rules owned by one team), whereas an open interface invites plug-ins (good for platform extension points).

5APIs / Features / Syntax

JDK / Spring APIStrategy it represents
Comparator<T>Ordering algorithm for sorting
java.util.function.*Generic algorithm shapes (Topic 1.2)
ThreadPoolExecutor + RejectedExecutionHandlerPolicy when the queue is full
javax.crypto.Cipher transformationsCipher algorithm selection
Spring Security PasswordEncoderHashing algorithm
Spring TaskScheduler TriggerScheduling algorithm (cron, periodic)
Resilience4j IntervalFunctionBack-off algorithm
Table 6 — Strategies in the platform
Strategy families you meet every day
Figure 4 — Strategy families you meet every day
JAVA
var executor = new ThreadPoolExecutor(8, 16, 60, SECONDS,
        new ArrayBlockingQueue<>(1_000),
        new ThreadPoolExecutor.CallerRunsPolicy());   // strategy for overload

6Practical Examples

6.1 Retry back-off strategies

JAVA
@FunctionalInterface
public interface BackOff { Duration delay(int attempt); }
 
BackOff fixed       = a -> Duration.ofMillis(200);
BackOff exponential = a -> Duration.ofMillis(100L << Math.min(a, 10));
BackOff jittered    = a -> exponential.delay(a)
        .plusMillis(ThreadLocalRandom.current().nextLong(100));

6.2 Testing a context with a stub strategy

JAVA
@Test
void totalAddsShippingFromPolicy() {
    ShippingPolicy fixed = (p, d) -> Money.eur("7.00");
    var checkout = new Checkout(fixed, TaxCalculator.NONE);
    assertThat(checkout.total(cartOf("40.00"), anyDest())).isEqualTo(Money.eur("47.00"));
}

6.3 Canary-releasing a new algorithm

JAVA
ShippingPolicy select(Customer c) {
    return flags.isEnabled("new-shipping", c.id())
            ? newZoneBasedPolicy
            : policies.forCustomer(c);
}

The new algorithm runs for a percentage of customers; metrics tagged by policy name compare results before full rollout.

7Common Problems & Mistakes

  • Context still branching on the strategy type (if (policy instanceof FlatRate)).
  • Strategies reaching into the context instead of receiving inputs.
  • Selection logic scattered across services instead of one registry.
  • Stateful strategies shared across threads.
  • null when no strategy matches — always provide a default.
  • Strategy with many methods used by many clients — likely a Bridge implementor.

8Performance & Production Considerations

AspectGuidance
Call overheadOne interface call; negligible, often inlined
Megamorphic call sitesMany implementations at one hot site reduce inlining — rarely significant
ObservabilityTag metrics and logs with the strategy name to compare behaviour
Configuration errorsValidate strategy parameters at startup (fail fast)
RolloutFeature-flag new strategies; keep old ones for rollback
Table 7 — Production guidance

9Architecture & Design Considerations

  • Strategies are the natural shape for business policies owned by domain teams.
  • Treat the strategy registry as a single, testable component; validate completeness (every segment has a policy).
  • Decide between closed (sealed) and open (plug-in) strategy families deliberately.
  • At system scale, strategies become configurable rules per tenant — version them and audit changes.

10Interview Questions & Answers

Q1Basic

What is the Strategy pattern?

Strategy is a behavioural pattern that defines a family of interchangeable algorithms, puts each behind a common interface, and lets the code that uses them choose one at run time without depending on its details. For example, a checkout service might calculate shipping costs with a StandardShipping, ExpressShipping or FreeShipping strategy, all implementing a ShippingCostStrategy interface. The context calls the interface and does not care which strategy it holds. New algorithms can be added without changing existing code, and each one can be tested on its own. In modern Java, simple strategies are often just lambdas.

Q2Basic

What are the roles in Strategy?

Strategy has three main roles. The strategy interface declares the operation that all algorithms provide, such as calculate(order). Concrete strategies implement that interface, each with a different algorithm, such as standard, express or free shipping. The context is the class that uses a strategy to do part of its work, such as the checkout service, and holds a reference to the strategy interface rather than any concrete class. In practice there is usually a fourth part: a client, selector or registry that decides which strategy the context should use, for example based on the customer's delivery option or a configuration value.

Q3Intermediate

Where should strategy selection happen?

Selection should happen in one clearly owned place, such as a registry, a factory or a configuration component. A common Spring approach is to inject all strategy beans into a map keyed by name, and have a selector choose by delivery option, tenant or feature flag. Keeping selection in one place means the rules for choosing are easy to find, test and change, and the context stays free of conditional logic. If selection logic is scattered, with if statements in the context and its callers, adding a strategy means hunting through the code and the pattern loses most of its benefit.

Q4 · [Intermediate] · Name three strategies from the JDK or Spring.
Answer: The JDK and Spring use Strategy widely. java.util.Comparator is a strategy for ordering, passed to sort methods that do not care how elements are compared. RejectedExecutionHandler is a strategy that a ThreadPoolExecutor uses when its queue is full, with implementations such as AbortPolicy and CallerRunsPolicy. In Spring Security, PasswordEncoder is a strategy for hashing and checking passwords, with BCrypt, Argon2 and delegating implementations. Other examples include Spring's TaskExecutor, retry back-off policies and Jackson's PropertyNamingStrategy. Recognising them helps you design your own strategies in the same style.
Q5Senior Engineer

How do records and sealed interfaces improve strategies?

Records make good strategy parameters and simple strategies. A strategy's configuration, such as a discount percentage and a cap, can be a record that validates itself in its compact constructor, so an invalid strategy cannot be created. Sealed interfaces close the family of strategies: the interface lists exactly which implementations are permitted, so the compiler can check that a switch over them is exhaustive, and no other team can quietly add an implementation. Together they turn a strategy family into a precise, compiler-checked model, which is well suited to business policies owned by one team, such as pricing or eligibility rules.

Q6Senior Engineer

How do you roll out a new algorithm safely?

Treat the new algorithm as a new strategy rather than changing the old one in place. Implement and test it alongside the existing strategy, then select it through a feature flag for a small share of traffic, for example 5% of customers or one region. Tag metrics and logs with the strategy name, so you can compare business and technical results, such as conversion rate, error rate and latency, between old and new. Increase the share gradually if the results are good. Keep the old strategy available, so rollback is a configuration change rather than a deployment, and remove it once the new one is fully proven.

Q7Technical Architect

When should a strategy family be open vs sealed?

Make a strategy family open when other teams need to extend it without changing your code, such as a platform extension point for payment providers, notification channels or export formats. An open family needs a stable, documented interface, versioning and validation of third-party implementations. Make it sealed when the family represents business policies owned and reviewed by one team, such as pricing rules or risk scores. Sealing means every variant is known, the compiler checks exhaustive handling, and new variants require a deliberate change by the owning team. Choose deliberately, because changing a widely used open interface later is expensive.

Q8Solution Architect

How would you design tenant-specific pricing policies across a SaaS platform?

Model each pricing policy as a strategy implementing a common interface, with its parameters held in validated records, so tenants choose policies and settings rather than writing code. Store each tenant's policy configuration in a versioned, audited store, so changes are traceable and can be rolled back. At load time, build a registry that maps tenants to configured strategies, falling back to a default policy when a tenant has none. Validate configurations before they go live, and expose metrics per policy and tenant, such as average price and error rates. For truly custom needs, offer a controlled extension mechanism rather than ad hoc code changes.

Newsletter

Get new tutorials every week

New ArchitectMind tutorials and interview questions, once a week. No spam, unsubscribe anytime.

Almost done! Check your inbox and click the confirmation link to complete your subscription.
Keep going in Book 5

Software Design & Design Patterns

Volume I · Topic 1.1

Software Design

Software Design Foundations & Principles

Volume I · Topic 1.2

Why Software Design Matters

Software Design Foundations & Principles

Volume I · Topic 1.3

Good vs Poor Design

Software Design Foundations & Principles

Volume II · Topic 1.1

Objects

Object-Oriented Design & SOLID

Volume II · Topic 1.2

Classes

Object-Oriented Design & SOLID

Volume II · Topic 1.3

Relationships

Object-Oriented Design & SOLID

Volume III · Topic 1.1

What Is a Design Pattern?

Creational & Structural Design Patterns

Volume III · Topic 1.2

Pattern Categories

Creational & Structural Design Patterns

Volume III · Topic 1.3

Pattern vs Framework

Creational & Structural Design Patterns

Volume IV · Topic 1.2

Strategy in Java

Behavioral Design Patterns

Volume IV · Topic 1.3

Strategy vs Conditional Logic

Behavioral Design Patterns

Volume V · Topic 1.1

Layered Architecture

Enterprise & Application Design Patterns

Volume V · Topic 1.2

Service Layer

Enterprise & Application Design Patterns

Volume V · Topic 1.3

Repository

Enterprise & Application Design Patterns

Volume VI · Topic 1.1

What Is Refactoring?

Refactoring, Code Quality & Evolutionary Design

Volume VI · Topic 1.2

Why Refactor?

Refactoring, Code Quality & Evolutionary Design

Volume VI · Topic 1.3

Refactoring Safety

Refactoring, Code Quality & Evolutionary Design

Volume VII · Topic 1.1

Method-Level Design

Advanced Design & Architecture-Level Design Decisions

Volume VII · Topic 1.2

Class-Level Design

Advanced Design & Architecture-Level Design Decisions

Volume VII · Topic 1.3

Module-Level Design

Advanced Design & Architecture-Level Design Decisions