Strategy mode and factory mode

The combination of policy pattern and factory pattern can eliminate the multi nesting of code if else

demand

There is such a demand for under store stores. There are four levels for user customers: ordinary customers, vip customers, super vip users and exclusive vip users. When users purchase goods, different discount strategies are adopted for different user levels and consumption amounts. In normal development, there will inevitably be multiple if else nested judgments. First, judge the level of users and then judge the consumption amount of the user's purchased goods.

malpractice

In addition, if there is a multi-level if else nesting in the above situation, if there is any further change in the demand, and another user level needs to be added, then the nesting judgment of if else will be added again. How to solve the above disadvantages? Using the combination of policy mode and factory mode can optimize the multi-level if else nesting

Realization

Write user level enumeration class

package com.zbiti.ifelse.UserType;

/**
 * User type enumeration class
 */
public enum UserPayServiceEnum {

    VIP(1,"Vip"),

    SUPERVIP(2,"SuperVip"),

    PARTICULALYVIP(3,"ParticularlyVip"),

    NORMAL(4,"NormalPayService");



    /**
     * State value
     */
    private int code;

    /**
     * Type description
     */
    private String value;

    private UserPayServiceEnum(int code, String value) {
        this.code = code;
        this.value = value;
    }
    public int getCode() {
        return code;
    }

    public String getValue() {
        return value;
    }

    public static UserPayServiceEnum valueOf(int code) {
        for (UserPayServiceEnum type : UserPayServiceEnum.values()) {
            if (type.getCode()==code) {
                return type;
            }
        }
        return null;
    }
    public static void main(String[] args) {
        System.out.println(UserPayServiceEnum.VIP.getValue());
    }

}

Write different user level policy classes

It should be noted that each policy class implements the InitializingBean interface to call the afterpropertieset method whenever the policy class is initiated and initialized by the spring container, and the function in this method is to save the corresponding user policy references to different user levels in the factory

Write discount interface

Different user level policy classes implement the interface, which contains discount methods

package com.zbiti.ifelse.UserType;

import java.math.BigDecimal;

public interface UserPayService {
    /**
     * Calculate price payable
     */
    public BigDecimal quote(BigDecimal orderPrice);
}

Write common user policy class

package com.zbiti.ifelse.UserType;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;

/**
 * Regular members do not discount the original price
 */

//Implement the InitializingBean interface. After the container starts, it will call the afterPropertiesSet() method to write the discount policy to the factory
@Service
public class NormalPayService implements UserPayService, InitializingBean {
    @Override
    public BigDecimal quote(BigDecimal orderPrice) {
        return new BigDecimal("10");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        UserPayServiceStrategyFactory.register(UserPayServiceEnum.NORMAL.getValue(), this);
    }

}

Write vip user policy class

package com.zbiti.ifelse.UserType;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;

/**
 * 10% off for ordinary members and 20% off for consumption over 100
 */

//Implement the InitializingBean interface. After the container starts, it will call the afterPropertiesSet() method to write the discount policy to the factory
@Service
public class VipPayService implements UserPayService, InitializingBean {
    @Override
    public BigDecimal quote(BigDecimal orderPrice) {
        if (orderPrice.compareTo(new BigDecimal("100")) > 1) {
            return new BigDecimal("8");
        }
        return new BigDecimal("9");
    }

    public void myShow(){
        System.out.println("myShow method invoke----->");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        UserPayServiceStrategyFactory.register(UserPayServiceEnum.VIP.getValue(), this);
    }

}

Write Super vip user policy class

package com.zbiti.ifelse.UserType;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;

/**
 * 20% off for super members
 */
@Service
public class SuperVipPayService implements UserPayService , InitializingBean {
    @Override
    public BigDecimal quote(BigDecimal orderPrice) {
        return new BigDecimal("8");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        UserPayServiceStrategyFactory.register(UserPayServiceEnum.SUPERVIP.getValue(),this);
    }
}

Write vip policy class for exclusive users

package com.zbiti.ifelse.UserType;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;

/**
 * 30% off for exclusive members
 */
@Service
public class ParticularlyVipPayService implements UserPayService, InitializingBean {
    @Override
    public BigDecimal quote(BigDecimal orderPrice) {
        if (orderPrice.compareTo(new BigDecimal("30"))>0) {
            return new BigDecimal("7");
        }
        return new BigDecimal("8");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        UserPayServiceStrategyFactory.register(UserPayServiceEnum.PARTICULALYVIP.getValue(),this);
    }
}

Write factory class

Pay attention to the register method in this factory. This method initializes bean in the spring container, that is, calling the register method in the afterPropertiesSet method after the completion of different user level policy classes. When the container is started, our spring container has a key for user level, which is map of user level policy class, and offers discount for different users. The policy class of the current user can be obtained according to the user level

package com.zbiti.ifelse.UserType;

import org.springframework.util.Assert;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Version 2: factory use (advanced version)
 */
//@Service
public class UserPayServiceStrategyFactory {

    private static Map<String,UserPayService> services = new ConcurrentHashMap<String,UserPayService>();

    public  static UserPayService getByUserType(String type){
        return services.get(type);
    }

    public static void register(String userType,UserPayService userPayService){
        Assert.notNull(userType,"userType can't be null");
        services.put(userType,userPayService);
    }
}

Write test class

package com.zbiti.ifelse;

import com.zbiti.ifelse.UserType.UserPayService;
import com.zbiti.ifelse.UserType.UserPayServiceStrategyFactory;
import com.zbiti.ifelse.UserType.VipPayService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.math.BigDecimal;

@SpringBootTest
@Slf4j
class IfElseApplicationTests {

    @Test
    void contextLoads() {
        calPrice();
    }

    public  void calPrice() {
        BigDecimal orderPrice = new BigDecimal("100");
        String vipType = "Vip";
        //Specify the user type to get the corresponding policy
        UserPayService strategy = UserPayServiceStrategyFactory.getByUserType(vipType);
        
//        UserPayService strategy2 = UserPayServiceStrategyFactory2.getByUserType(vipType);

        System.out.println(strategy);
//        System.out.println(strategy2);
        BigDecimal quote = strategy.quote(orderPrice);
        if(strategy instanceof VipPayService){
           ((VipPayService) strategy).myShow();
        }
        System.out.println(quote);
    }

}

Result

We can see that vip users have a 10% discount. In the different discount strategies adopted by different user levels when purchasing goods, we don't have a multi-layer nesting of if else

tips

The implementation of the factory class is one of the above (recommended). In addition, there are other ways. Please refer to the following

Write the policy to the map in advance, but you need to manually new the policy object here

package com.zbiti.ifelse.UserType;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * Version 1: factory use
 */
public class UserPayServiceStrategyFactory2 {

    private static Map<String,UserPayService> services = new ConcurrentHashMap<String,UserPayService>();

    public  static UserPayService getByUserType(String type){
        return services.get(type);
    }

    static{
        services.put(UserPayServiceEnum.VIP.getValue(), new VipPayService());
        services.put(UserPayServiceEnum.SUPERVIP.getValue(), new SuperVipPayService());
        services.put(UserPayServiceEnum.PARTICULALYVIP.getValue(), new ParticularlyVipPayService());
        services.put(UserPayServiceEnum.NORMAL.getValue(), new NormalPayService());
    }



}


You can also write factory classes by reflection

package com.zbiti.ifelse.UserType;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * Version 1: factory use
 */
public class UserPayServiceStrategyFactory3 {

    private static Map<String, Class<? extends UserPayService>> services = new ConcurrentHashMap<>();

    //Initialize map, store policy
    static {
        services.put(UserPayServiceEnum.VIP.getValue(), VipPayService.class);
        services.put(UserPayServiceEnum.SUPERVIP.getValue(), SuperVipPayService.class);
        services.put(UserPayServiceEnum.PARTICULALYVIP.getValue(), ParticularlyVipPayService.class);
        services.put(UserPayServiceEnum.NORMAL.getValue(), NormalPayService.class);
    }

    //Acquisition strategy
    public static UserPayService getByUserType(String type) {
        try {
            Class<? extends UserPayService> userPayServiceClass = services.get(type);
            return userPayServiceClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            return new NormalPayService();
        }

    }

}


In fact, it can also be used with annotations. You can customize an annotation class and mark annotations on the policy class (the value is different user level). When the container is started, you can scan our customized annotations and write them into the map.

Reference resources

if-else

if-else2

if-else3

This article is based on the platform of blog one article multiple sending OpenWrite Release!

Keywords: Programming Java Spring Lombok Junit

Added by supermars on Mon, 04 Nov 2019 03:51:54 +0200