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
This article is based on the platform of blog one article multiple sending OpenWrite Release!