Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The Strategy pattern lets Java code swap one algorithm for another through a shared contract. Use named classes when implementations need their own identity, state, or richer API; use a lambda when the strategy is a single operation. In either form, the context delegates the work—the code that constructs or configures it chooses the strategy.

What the Strategy pattern does

Strategy separates an algorithm that can vary from the class that uses it. The pattern has three roles:

  • Strategy contract: describes the operation the context needs.
  • Concrete strategies: implement that operation in different ways.
  • Context: holds a strategy and delegates the variable work to it. Client or configuration code supplies the selected implementation.

Because the context depends on the contract rather than concrete algorithms, a new variant can often be added without putting another algorithm branch into the context. The trade-off is extra structure and the need for some other part of the program to choose the appropriate strategy. Refactoring.Guru’s Java Strategy example illustrates these roles.

Implement it with named Java classes

A named interface and concrete classes make the pattern’s parts explicit. This illustrative pseudocode shows a checkout delegating pricing to its supplied strategy:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
interface PricingStrategy {
    Money price(Order order);
}

final class Checkout {
    private final PricingStrategy pricing;

    Checkout(PricingStrategy pricing) {
        this.pricing = pricing;
    }

    Money total(Order order) {
        return pricing.price(order);
    }
}

final class MemberPricing implements PricingStrategy {
    public Money price(Order order) {
        return order.subtotal().multiply(0.90);
    }
}

The example is illustrative, not a compiled program; types such as Money and Order are domain types that would need to be defined. In a real application, construction or configuration code would pass an implementation such as new MemberPricing() to Checkout.

Named implementations are useful when an algorithm has meaningful domain identity, substantial internal state, several related methods, or behavior that benefits from its own documentation. The Java language does not require a class for every strategy; the named form is a design choice that emphasizes explicit roles.

Replace a single-operation strategy with a lambda

If the strategy contract has one abstract operation, it can be a functional interface. Java’s functional-interface mechanism supplies target types for lambdas and method references; the lambda’s parameters and result correspond to the interface’s single abstract method. Oracle’s Java SE 24 java.util.function documentation describes both general-purpose JDK interfaces and their availability to user code.

@FunctionalInterface
interface PricingStrategy {
    Money price(Order order);
}

PricingStrategy memberPrice = order -> order.subtotal().multiply(0.90);
Checkout checkout = new Checkout(memberPrice);

This is also illustrative pseudocode, not a tested program. The domain-specific PricingStrategy name communicates what the behavior means. A standard type such as Function<Order, Money> can be shorter when the meaning is obvious at the call site, but may be less expressive when pricing is central to the domain. Lambda implementations are a concise alternative to separate strategy classes when the behavior is small and fits the contract. Refactoring.Guru’s Java example discusses that option.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A lambda defines behavior; invocation runs it

Evaluating a lambda does not immediately execute its body. The Java SE 26 Language Specification states: “Evaluation of a lambda expression produces an instance of a functional interface (§9.8). Lambda expression evaluation does not cause the execution of the expression’s body; instead, this may occur at a later time when an appropriate method of the functional interface is invoked.” In the example, the pricing calculation occurs when price(order) is called, not when the lambda is assigned to memberPrice. Oracle Java SE 26 Language Specification, Chapter 15.

Choose between a conditional, classes, and a lambda

Strategy is useful when algorithm variants are legitimate alternatives, evolve independently, or are selected at runtime. It can keep algorithm details out of the context and avoid an expanding conditional there. It is not automatically an improvement: for a small, stable choice, a straightforward conditional may be easier to follow.

Decision factor Often favors a conditional Often favors Strategy
Number and stability of variants A few branches that rarely change Several variants, or algorithms expected to evolve
Contract shape No useful shared operation to isolate One operation can use a lambda; multiple related operations or state may suit named implementations
Selection One fixed choice with no need for substitution Choice varies by runtime input or configuration
Clarity The branch is more obvious than introducing an abstraction Named domain behavior makes intent clearer and keeps algorithm details separate
Change isolation Algorithm and context are simple and change together Algorithms should change independently, or adding a variant should not require editing the context

These are design heuristics, not language rules. The key question is whether substituting the behavior makes the program easier to understand and change enough to justify the added abstraction and selection responsibility.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

A familiar Java example: Comparator

java.util.Comparator provides comparison behavior through compare(), and sorting code can receive a comparator to determine ordering. Refactoring.Guru cites Comparator#compare() used by Collections#sort() as a core Java example of strategy-like behavior. The point is the design shape: the sorting operation can delegate comparison to supplied behavior rather than hard-code every ordering rule. Refactoring.Guru’s Java Strategy page.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.