what? The code is full of if else

preface

"Have you been bothered by if else in the code?"

"Has the idea of sharpening the knife to it been premeditated for a long time?"
"But suffer from no good way?"
Come on, xdm, I'll make a fool of myself here, throw away the jade and post my solutions for everyone's reference.

Everyone must be familiar with the following codes. Of course, there are few if else like this. My understanding is that it is generally not necessary to optimize in the early stage, unless you know that there are many types or logic to deal with in the early stage, it is necessary to solve if else gracefully in the early stage.
Most of the time, the project has just started. First, the business itself is not clear. Second, the demand schedule of the early-stage project is relatively close. It is important to complete the development work as efficiently as possible. Although not all the code is a shuttle, looking back, you can't help but spray a word of shit when you see the code you write
, the business is mature, and after the project takes shape, more than 50% of the code can be optimized (or my level may be too low) ☹).


Whether it is the if else caused by the complexity of business logic design in the early stage or the if else piled up by Shishan code in the later stage, in my opinion, it is actually an application scenario of strategic thinking. Of course, if we use the strategic mode, we don't know how much we use, so it's very suitable to solve the above scenario.



Strategy mode

Common interface

First, for the if else scenario in the figure above, define an interface, a general payment interface class

public interface IPay {
    void pay();
} 

Then define an annotation to mark each strategy

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PayCode {
    String value();
    String name();
}

Concrete implementation class

Next is the concrete implementation class
Implementation class is a specific strategy. When we need to add a new strategy, such as meituan payment, we can directly add an implementation class, which not only conforms to the opening and closing principle we often say, but also realizes the decoupling of business logic. Don't look at the simple code below. After all, it's just an example. In the actual scene, this logic may make you confused and impossible to prevent.

@PayCode(value = "ali",name = "Alipay payment")
@Service
public class AliPay implements IPay {
    @Override
    public void pay() {
        System.out.println("--------Launch Alipay payment");
    }
}

@PayCode(value = "jd",name = "JD payment")
@Service(value = "jdPay")
public class JDPay implements IPay{
    @Override
    public void pay() {
        System.out.println("--------Launched JD payment");
    }
}

@PayCode(value = "weiXin",name = "Wechat payment")
@Service
public class WeiXinPay implements IPay {
    @Override
    public void pay() {
        System.out.println("--------Launched wechat payment");
    }
} 

Core processing class

The core logic of PayService2 is to inherit the ApplicationListener of Spring and use the listening event ContextRefreshedEvent to realize the preliminary loading of each specific implementation class.
The principle is that after the Spring IOC container loads and processes the corresponding Bean, there is an action to release the event (ContextRefreshedEvent). In fact, this is an extension provided by the Spring IOC container. xdm, reasoning is still very practical. This processing mechanism is very common in daily development and Research on the framework source code. Learn a wave ☺

@Service
public class PayService2 implements ApplicationListener<ContextRefreshedEvent> {
    private static Map<String,IPay> payMap = null;
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        Map<String,Object> beansWithAnno = applicationContext.getBeansWithAnnotation(PayCode.class);
        if (beansWithAnno != null){
            payMap = new HashMap<>();
            beansWithAnno.forEach((key,value) ->{
                String bizType = value.getClass().getAnnotation(PayCode.class).value();
                payMap.put(bizType,(IPay) value);
            });
        }
    }

    public void pay(String code){
        payMap.get(code).pay();
    }
}

Of course, in order to obtain the beans in the Spring container, do some necessary operations on the beans to achieve the effect of taking as you use. Of course, there is more than this usage.
I'm very considerate. I'll post another writing method for you.
PayService3 and PayService2 can get the same effect
Inherit ApplicationContextAware,
There is only one method in the ApplicationContextAware interface. If this method is implemented, spring will automatically execute this method when creating this implementation class and inject ApplicationContext into this class. In other words, spring needs to instantiate this class when it starts (if it is lazy loading, it is instantiated when you need it), When instantiating this class and finding that it contains the ApplicationContextAware interface, spin will call the setApplicationContext method of this object and set the ApplicationContext into it.
Knowledge point: generally, we often use this class to obtain the IOC container of Spring

@Service
public class PayService3 implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    private static final String SUFFIX = "Pay";
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void toPay(String payCode){
        ((IPay)applicationContext.getBean(getBeanName(payCode))).pay();
    }
    public String getBeanName(String payCode){
        return payCode + SUFFIX;
    }
}

Specific use class

/**
 * @description:
 * @author: zhanghailang
 * @date: 2022-1-24 14:43
 */
@RestController
public class TestContrller {

    @Autowired
    private PayService2 payService2;

    @Autowired
    private PayService3 payService3;

    @PutMapping(value = "/test/{payCode}")
    public void test(@PathVariable("payCode") String payCode){

//        if ("ali".equals(payCode)) {
//            System.out.println("ali pay start");
//        }
//        if ("jd".equals(payCode)) {
//            System.out.println("jd pay start");
//        }
//        if ("weixin".equals(payCode)) {
//            System.out.println("weixin pay start");
//        }
        //.....

        this.payService2.pay(payCode);
        this.payService3.toPay(payCode);
    }
}

The result of the request is as follows:






Through the above demonstration, you should be able to see the advantages and disadvantages of this strategy mode to reduce if else
Advantages: the code is clean and decoupled, conforms to the opening and closing principle, has clear responsibilities and elegant code
Disadvantages: each implementation needs to add a new class. When there are too many policies, we should consider using mixed mode to optimize the policy mode. "



Functional programming

『 ☺ Hey, didn't you expect that you can still use functional programming? Let me explain it for you a little. "

FunctionPayService and PayService4 use the @ PostConstruct, supplier < > interface to encapsulate the defined specific business operation class FunctionPayService in the map according to different strategies. They can be used and taken as needed.

/**
 * @description: Specific business classification
 * @author: zhanghailang
 * @date: 2022/1/25 17:18
 */
@Service
public class FunctionPayService {
    public String aliPay() {
        System.out.println("ali Pay start");
        return "ali";
    }

    public String weiXinPay() {
        System.out.println("weiXin Pay start");
        return "weinXin";
    }

    public String jdPay() {
        System.out.println("jd Pay start");
        return "jd";
    }
}


/**
 * @description: Use Lamda functional programming to reduce if else
 * Official account No. https://mp.weixin.qq.com/s/79go9AFZgcnNM7uk_5Yaew
 * @author: zhanghailang
 * @date: 2022/1/25 17:09
 */
@Service
public class PayService4 {

    @Autowired
    private FunctionPayService functionPayService;

    private Map<String, Supplier<String>> payMap = new HashMap<>();

    /**
     * Initialize the business dispatch logic instead of the if else part
     */
    @PostConstruct
    public void dispatcherInit() {
        payMap.put("ali",() -> functionPayService.aliPay());
        payMap.put("weiXin",() -> functionPayService.weiXinPay());
        payMap.put("jd",() -> functionPayService.jdPay());
    }

    public void pay(String payCode){
        this.payMap.get(payCode).get();
    }
}

The result of reducing if else in the above functional programming is the same as that in the strategy mode.
Functional programming is really easy to use. If you don't understand xdm, it's recommended to learn a wave, but it's not recommended to abuse it. I've seen a piece of code, functional programming, pure Lamda expression and various business logic. I'm dizzy. It's strongly not recommended to write a lot of business logic in functional programming.

The above is my humble opinion. Happy New Year

Keywords: Java Spring

Added by capitala on Fri, 28 Jan 2022 16:37:48 +0200