Fundamentals 8 min read

Mastering the Strategy Pattern: From Simple IF‑ELSE to Scalable Design

This article explains why hard‑coded if‑else logic hinders extensibility, introduces the Strategy pattern, shows its structure and Java implementation for data synchronization, then enhances the design with a Factory to decouple object creation, concluding with best‑practice recommendations.

DaTaobao Tech
DaTaobao Tech
DaTaobao Tech
Mastering the Strategy Pattern: From Simple IF‑ELSE to Scalable Design

Problem with if‑else for multiple external systems

In a project that exchanges data with several external systems, the initial implementation used a chain of if‑else statements to select the target system. As the number of integrations grew, the code became difficult to maintain, violated the Single‑Responsibility Principle and the Open/Closed Principle.

Strategy pattern

The Strategy pattern defines a family of interchangeable algorithms encapsulated in separate classes. A client can switch the concrete implementation at runtime without changing its own code.

Core structure

Define a strategy interface (or abstract class) that declares the operations required by all strategies.

Implement a concrete strategy class for each external system.

Create a context class that holds a reference to a DataProcessingStrategy and delegates calls.

In client code, instantiate the desired concrete strategy and pass it to the context.

Concrete example for data sync

public interface DataProcessingStrategy {
    void receiveData();
    void sendData(String data);
}
public class ASystemDataProcessingStrategy implements DataProcessingStrategy {
    @Override
    public void receiveData() {
        // implementation for system A
    }
    @Override
    public void sendData(String data) {
        // implementation for system A
    }
}
public class BSystemDataProcessingStrategy implements DataProcessingStrategy {
    @Override
    public void receiveData() {
        // implementation for system B
    }
    @Override
    public void sendData(String data) {
        // implementation for system B
    }
}
public class Context {
    private DataProcessingStrategy strategy;
    public Context(DataProcessingStrategy strategy) {
        this.strategy = strategy;
    }
    public void setStrategy(DataProcessingStrategy strategy) {
        this.strategy = strategy;
    }
    public void sendData(String data) {
        strategy.sendData(data);
    }
    public void receiveData() {
        strategy.receiveData();
    }
}
public class Main {
    public static void main(String[] args) {
        Context context = new Context(new ASystemDataProcessingStrategy());
        context.sendData("payload for A");
        context.receiveData();

        context.setStrategy(new BSystemDataProcessingStrategy());
        context.sendData("payload for B");
        context.receiveData();
    }
}

Limitations of a pure Strategy implementation

Hard‑coded dependencies: the client must know concrete strategy classes, so adding a new system requires code changes.

Tight coupling between client and concrete strategies increases maintenance effort.

Combining with the Factory pattern

Introduce a factory that registers and retrieves strategies by name, removing the need for the client to reference concrete classes.

public class DataProcessingStrategyFactory {
    private static final ConcurrentHashMap<String, DataProcessingStrategy> strategies = new ConcurrentHashMap<>();

    public static void register(String name, DataProcessingStrategy strategy) {
        strategies.put(name, strategy);
    }

    public static DataProcessingStrategy getStrategy(String name) {
        return strategies.get(name);
    }
}

Registration can be performed at application startup:

DataProcessingStrategyFactory.register("A", new ASystemDataProcessingStrategy());
DataProcessingStrategyFactory.register("B", new BSystemDataProcessingStrategy());

Client usage:

public class Main {
    public static void main(String[] args) {
        DataProcessingStrategy a = DataProcessingStrategyFactory.getStrategy("A");
        a.sendData("payload for A");
        a.receiveData();

        DataProcessingStrategy b = DataProcessingStrategyFactory.getStrategy("B");
        b.sendData("payload for B");
        b.receiveData();
    }
}

Takeaway

Applying the Strategy pattern eliminates repetitive conditional logic and makes the codebase extensible. Adding a factory further decouples client code from concrete implementations, reducing hard‑coded dependencies. Use these patterns judiciously to avoid unnecessary complexity.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Design PatternsJavaSoftware ArchitectureStrategy PatternFactory PatternObject-Oriented Design
DaTaobao Tech
Written by

DaTaobao Tech

Official account of DaTaobao Technology

0 followers
Reader feedback

How this landed with the community

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.