Use policy patterns elegantly with the power of Spring


What ispageThe strategy pattern

Strategy Pattern tutorial: Strategy Pattern

Definition: An object that has a behavior that can be implemented by different algorithms in different scenarios. In the case of similar algorithms, if… else… The resulting complexity and difficult to maintain. Key code: implement the same interface.


Application scenarios

public Integer getResult(Integer num1, Integer num2, OperationTypeEnum typeEnum) {
	// Determine the operation type
	if (OperationTypeEnum.ADD == typeEnum) {
		/ / add
		return num1 + num2;

	} else if (OperationTypeEnum.SUBTRACT == typeEnum) {
		/ / by subtracting
		return num1 - num2;

	} else if (OperationTypeEnum.MULTIPLY == typeEnum) {
		/ / multiplied
		return num1 * num2;
	}
	throw new RuntimeException("No corresponding operation type");
}
Copy the code

Scenario: There are two Integer numbers num1 and num2, and the corresponding adding, subtracting, and multiplying logic (algorithm implementation) is performed based on the operation type (scenario) of the input parameters. In this way, our application scenario corresponds to the policy pattern definition described above. Disadvantages: With the complexity of implementation logic, and the expansion of operation types, if… else… Logical blocks become more and more bulky and difficult to maintain.


Elegant strategy pattern

The rookie tutorial implements a classic strategy mode solution for the above scenario. This article, however, aims to elegantly implement the policy pattern by leveraging Spring’s @AutoWired annotation to directly inject the collection types of classes.

  1. Add, subtract, and multiply to implement the same interfaceOperationServiceThe same method ofdoOperation().
public interface OperationService {
    /** * Perform the operation **@param num1
     * @param num2
     * @return* /
    Integer doOperation(Integer num1, Integer num2);
}
Copy the code
@Service("addOperationServiceImpl")
public class AddOperationServiceImpl implements OperationService {
    /** * Perform the add operation **@param num1
     * @param num2
     * @return* /
    @Override
    public Integer doOperation(Integer num1, Integer num2) {
        returnnum1 + num2; }}Copy the code
@Service("subtractOperationServiceImpl")
public class SubtractOperationServiceImpl implements OperationService {
    /** * Perform the subtraction operation **@param num1
     * @param num2
     * @return* /
    @Override
    public Integer doOperation(Integer num1, Integer num2) {
        returnnum1 - num2; }}Copy the code
@Service("multiplyOperationServiceImpl")
public class MultiplyOperationServiceImpl implements OperationService {
    /** * perform the multiplication operation **@param num1
     * @param num2
     * @return* /
    @Override
    public Integer doOperation(Integer num1, Integer num2) {
        returnnum1 * num2; }}Copy the code
  1. Three kinds of operationtypeAnd operatingImplement the name of the class Bean, maintain to enumerationOperationTypeEnumIs used as a dictionary.
public enum OperationTypeEnum {
    /** * add */
    ADD(1."Together"."addOperationServiceImpl"),
    /** ** subtracts */
    SUBTRACT(2."Subtraction"."subtractOperationServiceImpl"),
    /** ** ** /
    MULTIPLY(3."Multiply"."multiplyOperationServiceImpl");

    /** * unique encoding */
    private Integer code;
    /** * description */
    private String desc;
    /** * The name of the corresponding policy pattern implementation class Bean */
    private String strategyBeanName;

    /* Constructor */

    /* getter & setter */
}
Copy the code

As in the code above, the name of the operation implementation class Bean, identified in the @Service annotation parentheses above each implementation class, is mapped to the operation type through the enumeration OperationTypeEnum.

  1. Calculate the operation policy interfaceCalculateOperationStrategyThere’s only one waycalculate(). I don’t need to talk about interfaces anymore, but just look at the implementation classes.
@Service("calculateOperationStrategy")
public class CalculateOperationStrategyImpl implements CalculateOperationStrategy {
    /** * 'Inject all beans of type OperationService into a Map * whose key is Bean name */
    @Autowired
    private Map<String, OperationService> operationServiceMap;

    /** * count **@param num1
     * @param num2
     * @param type
     * @return* /
    @Override
    public Integer calculate(Integer num1, Integer num2, OperationTypeEnum type) {
        // The name of the policy pattern implementation class Bean corresponding to this operation type
        String strategyBeanName = type.getStrategyBeanName();
        if(operationServiceMap.get(strategyBeanName) ! =null) {
            // Execute the doOperation method of the corresponding policy pattern implementation class
            return operationServiceMap.get(strategyBeanName).doOperation(num1, num2);

        } else {
            throw new RuntimeException("No corresponding operation type"); }}Copy the code
  • We’re through@AutowiredThe interface directly sets all types toOperationServiceIs injected into a Map of the BeankeyThe Bean’s name. See my last article for a detailed implementation of this step:[Spring] @autoWired A deep anatomy of the collection types of injected classes.
  • We just know the enumeration of the operation type passed inOperationTypeEnumSo we can use this name as the key, get the implementation class of the corresponding operation type from the Map, and finally call the implementation class of the corresponding operation type directlydoOperation()Methods.

  1. Finally, the test class
@RunWith(SpringRunner.class)
@SpringBootTest(classes = PpValidatorApplication.class)
public class CalculateOperationStrategyTest {
    @Autowired
    private CalculateOperationStrategy calculateOperationStrategy;

    @Test
    public void testCalculate(a) {
        Integer num1 = 66;
        Integer num2 = 6;

        System.out.println("Calculation method:" 
                + OperationTypeEnum.ADD.getDesc()
                + " ---> 计算结果:" 
                + calculateOperationStrategy.calculate(num1, num2, OperationTypeEnum.ADD));

        System.out.println("Calculation method:" 
                + OperationTypeEnum.SUBTRACT.getDesc()
                + " ---> 计算结果:" 
                + calculateOperationStrategy.calculate(num1, num2, OperationTypeEnum.SUBTRACT));

        System.out.println("Calculation method:" 
                + OperationTypeEnum.MULTIPLY.getDesc()
                + " ---> 计算结果:"+ calculateOperationStrategy.calculate(num1, num2, OperationTypeEnum.MULTIPLY)); }}Copy the code

The result is as follows

Calculation method: add --> result: 72 Calculation method: subtract --> result: 60 Calculation method: multiply --> result: 396Copy the code

!!!!!!!!! Perfect!!


conclusion

  • Policy patterns can perform implementation logic corresponding to the type in the program context. Subsequent modification and replacement are very convenient.
  • The policy pattern can be elegantly implemented with Spring’s @AutoWired annotation, which allows direct injection of class collection types. [Spring] @autowired Injection class set type deep anatomy.
  • Each implementation class only needs to maintain the contents of its own classdoOperation()Method can.
  • To extend the type, simply add an implementation class, the implementationOperationServiceInterface, and maintains the name and type of the implementation class Bean into the dictionary enumerationOperationTypeEnumIn the.

HelloLittleRain: A simple book to share