Fundamentals 13 min read

Applying the Strategy Pattern in Java with Spring Integration

This article explains the Strategy design pattern in Java, demonstrates its integration with Spring using custom annotations, enums, and a context manager, provides complete code examples, discusses its advantages over traditional conditional logic, and shows how to easily add new strategies.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Applying the Strategy Pattern in Java with Spring Integration

Strategy Pattern: Define interchangeable algorithms to simplify adding, removing, or modifying business logic while keeping the calling code unaware of the specific implementation.

1. When to Use the Strategy Pattern

Business Scenario

In a project, different types of processing orders require distinct complex validation logic.

Why Use It

The order type is unlikely to change, but future requirements may need state synchronization or data syncing; currently only validation is needed.

Initially, using conditional statements caused the Service class to balloon to hundreds of lines, making it hard to locate methods.

To improve readability and accommodate future changes, the logic is encapsulated in separate strategy classes.

2. Using the Strategy Pattern in Java

Reference: Beginner tutorial on the Strategy pattern.

3. Drawbacks of the Classic Strategy Implementation

If there is only a single execution path, conditional checks are still required.

Adding a new strategy class still forces modifications to existing code.

Creating a new object for each call increases memory usage and execution time.

Even small inefficiencies can add up in a large system, so careful attention to these details is important.

4. Optimizing the Strategy Pattern with Spring

Idea

Replace conditional logic with a Map that retrieves the appropriate strategy based on type.

Use a custom annotation together with Spring to automatically register new strategies without code changes.

Leverage Spring to manage strategy beans, avoiding repeated object creation.

Implementation

Custom Annotation

package com.dakai.strategypattern.annotation;

import java.lang.annotation.*;

/**
 * Business strategy annotation
 * @author ChenErGao
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Inherited
public @interface BusinessStrategy {}

Enum for Strategy Types

package com.dakai.strategypattern.enums;

/**
 * @author ChenErGao
 */
public enum StrategyEnum {
    /** Strategy A */
    STRATEGY_A(1, "Strategy A"),
    /** Strategy B */
    STRATEGY_B(2, "Strategy B"),
    /** Strategy C */
    STRATEGY_C(3, "Strategy C");

    private int strategyType;
    private String typeDescription;

    StrategyEnum(int strategyType, String typeDescription) {
        this.strategyType = strategyType;
        this.typeDescription = typeDescription;
    }
    public int getStrategyType() { return strategyType; }
    public String getTypeDescription() { return typeDescription; }
}

Result Wrapper

package com.dakai.strategypattern.strategy.domain;

/**
 * Business processing result wrapper
 * @author ChenErGao
 */
public class BizResult<T> {
    private boolean isSuccess = true;
    private T result;
    public BizResult() {}
    public BizResult(boolean isSuccess) { this.isSuccess = isSuccess; }
    public BizResult(T result) { this.result = result; }
    public BizResult(boolean isSuccess, T result) { this.isSuccess = isSuccess; this.result = result; }
    public boolean isSuccess() { return isSuccess; }
    public void setSuccess(boolean success) { isSuccess = success; }
    public T getResult() { return result; }
    public void setResult(T result) { this.result = result; }
    @Override
    public String toString() {
        return "BizResult{" + "isSuccess=" + isSuccess + ", result=" + result + '}';
    }
}

Strategy Interface

package com.dakai.strategypattern.strategy;

import com.dakai.strategypattern.annotation.BusinessStrategy;
import com.dakai.strategypattern.enums.StrategyEnum;
import com.dakai.strategypattern.strategy.domain.BizResult;
import com.dakai.strategypattern.strategy.domain.StrategyParam;

/**
 * @author ChenErGao
 */
public interface Strategy {
    /**
     * Invoke the strategy
     * @param strategyParam parameters for execution
     * @return wrapped business result
     */
    BizResult invokeStrategy(StrategyParam strategyParam);
    /**
     * Get the enum corresponding to this strategy
     * @return strategy enum
     */
    StrategyEnum getStrategyEnum();
}

Strategy Implementations (A, B, C)

package com.dakai.strategypattern.strategy.impl;

import com.dakai.strategypattern.annotation.BusinessStrategy;
import com.dakai.strategypattern.enums.StrategyEnum;
import com.dakai.strategypattern.strategy.Strategy;
import com.dakai.strategypattern.strategy.domain.BizResult;
import com.dakai.strategypattern.strategy.domain.StrategyParam;
import org.springframework.stereotype.Component;

@Component
@BusinessStrategy
public class StrategyA implements Strategy {
    @Override
    public BizResult invokeStrategy(StrategyParam strategyParam) {
        System.out.println("Executing StrategyA......");
        return null;
    }
    @Override
    public StrategyEnum getStrategyEnum() { return StrategyEnum.STRATEGY_A; }
}

@Component
@BusinessStrategy
public class StrategyB implements Strategy {
    @Override
    public BizResult invokeStrategy(StrategyParam strategyParam) {
        System.out.println("Executing StrategyB......");
        return null;
    }
    @Override
    public StrategyEnum getStrategyEnum() { return StrategyEnum.STRATEGY_B; }
}

@Component
@BusinessStrategy
public class StrategyC implements Strategy {
    @Override
    public BizResult invokeStrategy(StrategyParam strategyParam) {
        System.out.println("Executing StrategyC......");
        return null;
    }
    @Override
    public StrategyEnum getStrategyEnum() { return StrategyEnum.STRATEGY_C; }
}

Strategy Context

package com.dakai.strategypattern.strategy;

import com.dakai.strategypattern.enums.StrategyEnum;
import com.dakai.strategypattern.strategy.domain.BizResult;
import com.dakai.strategypattern.strategy.domain.StrategyParam;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ChenErGao
 * @date 2019/8/27 11:25 PM
 */
@Component
public class StrategyContext {
    public Map<StrategyEnum, Strategy> strategyMap = new HashMap<>(3);
    /**
     * Execute a strategy based on the provided parameters
     */
    public BizResult invokeContext(StrategyParam strategyParam) {
        if (strategyParam == null) {
            throw new IllegalArgumentException("Strategy parameter cannot be null!");
        }
        StrategyEnum strategyEnum = strategyParam.getStrategyEnum();
        Strategy strategy = strategyMap.get(strategyEnum);
        return strategy.invokeStrategy(strategyParam);
    }
}

Strategy Manager (BeanPostProcessor)

package com.dakai.strategypattern.manage;

import com.dakai.strategypattern.annotation.BusinessStrategy;
import com.dakai.strategypattern.strategy.Strategy;
import com.dakai.strategypattern.strategy.StrategyContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

/**
 * Strategy class manager
 * @author ChenErGao
 */
@Component
public class StrategyManage implements BeanPostProcessor {
    @Autowired
    private StrategyContext strategyContext;
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Strategy && bean.getClass().isAnnotationPresent(BusinessStrategy.class)) {
            Strategy strategy = (Strategy) bean;
            strategyContext.strategyMap.put(strategy.getStrategyEnum(), strategy);
        }
        return bean;
    }
}

Test Class

package com.dakai.strategypattern;

import com.dakai.StrategyPatternApplication;
import com.dakai.strategypattern.enums.StrategyEnum;
import com.dakai.strategypattern.strategy.StrategyContext;
import com.dakai.strategypattern.strategy.domain.StrategyParam;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = StrategyPatternApplication.class)
public class StrategyTest {
    @Autowired
    private StrategyContext strategyContext;
    @Test
    public void test() {
        StrategyParam p1 = new StrategyParam();
        p1.setStrategyEnum(StrategyEnum.STRATEGY_A);
        strategyContext.invokeContext(p1);
        StrategyParam p2 = new StrategyParam();
        p2.setStrategyEnum(StrategyEnum.STRATEGY_B);
        strategyContext.invokeContext(p2);
        StrategyParam p3 = new StrategyParam();
        p3.setStrategyEnum(StrategyEnum.STRATEGY_C);
        strategyContext.invokeContext(p3);
    }
}

Execution Result

Executing StrategyA......
Executing StrategyB......
Executing StrategyC......

3. How to Add a New Strategy

Define a new class that implements the Strategy interface, annotate it with @BusinessStrategy, and add a corresponding value to StrategyEnum. No other code changes are required.

5. Summary

Reduced memory consumption.

Convenient addition of new strategy classes.

More flexible execution without explicit if statements.

Source code: https://github.com/createdByCrg/StrategyPattern

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 PatternsStrategy Patternspring
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

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.