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.
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
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
