Strategy Pattern Example

In writing the business logic of Java projects, it is often encountered that there are many branches in a business code block, and the execution logic of each branch is relatively complex. The traditional method is to use nested if, else, or switch statements to handle this, but the disadvantage of this approach is that the business code has a high degree of coupling. Adding or modifying a branch requires significant changes to the existing code, and the code readability is poor and not elegant.

Using the strategy pattern can effectively avoid the above situations. It defines a family of algorithms, encapsulates each one, and makes them interchangeable. This pattern ensures that changes in algorithms do not affect the clients using them.

Strategy Pattern Structure Diagram

image-20231128095507347
  • Strategy: Defines the common interface for all supported algorithms.
  • ConcreteStrategy: Encapsulates specific algorithms or behaviors, inheriting from Strategy.
  • Context: Configured with a ContextStrategy, maintaining a reference to a Strategy object.

Strategy Pattern Implementation

The strategy pattern needs to be based on specific business scenarios. Taking the execution of algorithms as an example, the system has built-in MATLAB, Python, and Java algorithms. When a calculation method is called, a specific algorithm is executed based on the input parameters, and the calculation result is returned.

Define Interface

Define the common interface for all supported algorithms.

/**
 * Interface for executing algorithms
 */
public interface ICalcExecService {

    /**
     * Execute algorithm
     * @param algorithmName Algorithm file name
     * @param data Algorithm input parameters
     * @return Algorithm execution result
     */
    public String execCalc(String algorithmName, double [][] data);

    /**
     * Execute Python algorithm
     * @param algorithmName Algorithm file name
     * @param data Algorithm input parameters
     * @return Algorithm execution result
     */
    public String execCalc(String algorithmName, double [][] data, String [] params);
}

Define Algorithm Constants

/**
 * Calculation constant class
 */
public class CalcConstant {
    /** Algorithm type Java */
    public static final String JAVA = "Java";

    /** Algorithm type Python */
    public static final String PYTHON = "Python";

    /** Algorithm type Matlab */
    public static final String MATLAB = "Matlab";
}

Define Strategy Factory

Use a ContextStrategy to configure and maintain a reference to a Strategy object.

/**
 * Strategy factory, instantiates strategy classes
 */
@Service
public class StrategyFactory {

    private StrategyFactory() {
    }

    private static final Map<String, ICalcExecService> strategyMap = new HashMap<>();

    /**
     * Register strategy
     */
    public static void registerStrategy(String operation, ICalcExecService calcExecService) {
        strategyMap.put(operation, calcExecService);
    }

    /**
     * Get strategy instance
     */
    public static ICalcExecService getStrategy(String operation) {
        return strategyMap.get(operation);
    }
}

Define Strategy Implementation Classes

Encapsulate specific algorithms or behaviors, inheriting from Strategy.

/**
 * Java calculation execution service implementation
 */
@Service
@Slf4j
public class CalcExecJavaService implements ICalcExecService, InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        StrategyFactory.registerStrategy(CalcConstant.JAVA, this);
    }

    /**
     * Execute Java algorithm
     * @param algorithmName Algorithm file name
     * @param data Algorithm input data
     * @return Algorithm execution result
     */
    @Override
    public String execCalc(String algorithmName, double [][] data) {
        return execCalc(algorithmName, data, null);
    }

    /**
     * Execute Java algorithm
     * @param algorithmName Algorithm file name
     * @param data Algorithm input data
     * @return Algorithm execution result
     */
    @Override
    public String execCalc(String algorithmName, double [][] data, String [] params) {
        log.info("Starting to execute JAVA algorithm with file name: {}", algorithmName);
        // todo: Specific algorithm execution logic, result
        return result;
    }
}
/**
 * Python calculation execution service implementation
 */
@Service
@Slf4j
public class CalcExecPythonService implements ICalcExecService, InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        StrategyFactory.registerStrategy(CalcConstant.PYTHON, this);
    }

    /**
     * Execute Python algorithm
     * @param algorithmName Algorithm file name
     * @param data Algorithm input data
     * @return Algorithm execution result
     */
    @Override
    public String execCalc(String algorithmName, double [][] data) {
        return execCalc(algorithmName, data, null);
    }

    /**
     * Execute Python algorithm
     * @param algorithmName Algorithm file name
     * @param data Algorithm input data
     * @param params Algorithm custom parameters
     * @return Algorithm execution result
     */
    @Override
    public String execCalc(String algorithmName, double [][] data, String [] params) {
        log.info("Starting to execute PYTHON algorithm with file name: {}", algorithmName);
        // todo: Specific algorithm execution logic, result
        return result;
    }
}

Call Algorithm

// Get strategy instance from the factory based on the input algorithm type
ICalcExecService calcExecService = StrategyFactory.getStrategy(CalcConstant.PYTHON);
// Pass algorithm name and parameters, execute calculation method to get the result
String result = calcExecService.execCalc(algorithmName, data);

Summary

The above example code perfectly implements the strategy pattern. Through the strategy factory, strategy implementation classes can be registered in the context when the container is loaded. When needed, strategy instances are obtained based on the algorithm type to execute the algorithm. Different algorithms are completely decoupled. If a new algorithm is added, only a new strategy instance needs to be added, which does not impact the existing business code, making it more elegant and readable.

By Tim

Leave a Reply

Your email address will not be published. Required fields are marked *