java 112 lines · 4 tabs

Runtime Strategy Selection for Shipping-Cost Calculators via a Factory

Shared by codesnips Sep 2026
4 tabs
package com.shop.shipping;

import java.math.BigDecimal;

public interface ShippingCostCalculator {

    ShippingMethod supports();

    BigDecimal calculate(ShipmentRequest request);
}

enum ShippingMethod {
    FLAT_RATE, WEIGHT_BASED, EXPRESS
}
4 files · java Explain with highlit

This snippet shows how the Strategy pattern and a small factory cooperate to select a shipping-cost algorithm at runtime, a common need when the same order must be priced differently depending on carrier, region, or promotion. The idea is to encode each pricing rule as its own class behind a shared contract, then defer the choice of which class to use until the request is actually being handled.

In ShippingCostCalculator, the contract is defined as a single behavioural method calculate plus a supports method that reports which ShippingMethod the strategy implements. Pairing the algorithm with a self-declared identity is what lets the factory build itself from the available implementations rather than from a hand-maintained switch statement. Each strategy is a small, independently testable unit; FlatRateCalculator returns a constant, while WeightBasedCalculator scales a per-kilogram rate and clamps to a minimum charge so tiny parcels are never underpriced.

ShippingCalculatorFactory is the runtime dispatcher. Its constructor receives every ShippingCostCalculator bean that Spring discovers on the classpath and indexes them into an EnumMap keyed by the ShippingMethod each one supports. Using Collectors.toMap with a merge function that throws makes duplicate registrations fail fast at startup instead of silently shadowing one strategy with another. Lookups in forMethod are O(1), and an unknown method raises an IllegalArgumentException rather than returning a misleading default.

ShippingController ties it together: it resolves the strategy with factory.forMethod(...), invokes calculate, and returns the price. The controller has no knowledge of individual algorithms, which is the whole point — the branching logic that would normally accumulate in the controller has been pushed into polymorphism.

The main trade-off is indirection: for two trivial cases this is more machinery than a simple conditional. It pays off when rules multiply, when each rule has its own dependencies, or when new carriers must be added without editing existing code. Adding a strategy means writing one class and declaring it a bean; the factory wires it automatically, satisfying the open/closed principle. A pitfall to watch is forgetting that every ShippingMethod needs a corresponding strategy, so a startup validation check is often added alongside this pattern.


Related snips

Share this code

Here's the card — post it anywhere.

Runtime Strategy Selection for Shipping-Cost Calculators via a Factory — share card
Link copied