scenario

At this point, there is a scenario where you need to design a different business process based on different states and conditions.

This may not make sense to you. For example, now merchants on the streets have adopted the payment method of converged payment. Converged payment means that a QR code supporting Alipay, wechat, JINGdong Wallet, UnionPay and so on is placed in front of the merchant counter, and users can pay through any payment APP.

solution

Approach 1.

Define enumeration types for each payment channel

public enum PayWay {
    ALI_PAY,

    WECHAT_PAY;
}
Copy the code

Annotations are then defined on each corresponding service to indicate which payment method corresponds

@Pay(PayWay.ALI_PAY)
public class AliPayServiceImpl implements PayService  {}
Copy the code

However, after careful consideration, there are still some problems

  1. If a payment method needs to be modified after being added,PayWayThis enumerated type
  2. In the program, you still need to do it according to different conditionsif elsejudgePayWayTo increase the payment method, it is necessary to modify the original judgment logic. The pseudocode is as follows
if("xxx"= ="aliPay"){
    
} else if("xxx"= ="wechatPay"){
    
}
// If you add the payment method, add else if
Copy the code

Thinking (2)

There are some problems in thinking ①, the first is if else judgment problem. So let’s think about what does this if else do?

If else is used to determine which payment method is adopted.

We can isolate this piece of code, let the corresponding business implementation class implement its own logical implementation, and then decide whether to filter out the business implementation class based on the return value true or false. The interface is defined as follows, and the SupportBean is a encapsulated entity

boolean isSupport(SupportBean supportBean);
Copy the code

Then each business implementation class implements its own isSupport method, pseudo code is as follows

@Override
public boolean isSupport(SupportBean supportBean) {
    if (supportBean.getType() == "xxx") {return true;
    }
    
    return false;
}
Copy the code

design

Note: Only one shelf is provided

The interface definition

Service interface definition, a business execution method execute (add parameters), an isSupport method (return true or false)

public interface Service {

    void execute(a);

    boolean isSupport(SupportBean supportBean);
}
Copy the code

Business implementation class

The execute method here just prints the string on the console. The isSupport method evaluates whether the remainder of supportNum in the SupportBean is equal to 0. If yes, the isSupport method returns true.

There are two similar implementations that I won’t post here.

@Component
public class AServiceImpl implements Service {
    @Override
    public void execute(a) {
        System.out.println("A execute");
    }

    @Override
    public boolean isSupport(SupportBean supportBean) {
        return supportBean.getSupportNum() % 3= =0; }}Copy the code

Next, define a help class

Helper classes

@Component
public class Helper {

    @Autowired
    private List<Service> services;

    public void execute(SupportBean supportBean){

        Service s = services.stream()
                .filter((service) -> service.isSupport(supportBean))
                .findFirst()/ / NPE anomalies
                .orElse(null);


        if(s ! =null){ s.execute(); }}}Copy the code

The execute method of the tool class is used to obtain the execution result of the corresponding service implementation class and verify the incoming parameters.

Note that findFirst() of a Lambda expression raises a NullPointException. If findFirst is called, NullPointException will be thrown. If findFirst is called, NullPointException will be thrown. You can change the above code to the following code to avoid NPE

Service s = services.stream()
        .filter((service) -> service.isSupport(supportBean))
        .map(Optional::ofNullable)
        .findFirst()
        .flatMap(Function.identity())
        .orElse(null);
Copy the code

test

Add a SpringBoot test class and a test method.

Call the execute method of the Helper Helper in the contextLoads test

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {

    @Autowired
    private Helper Helper;

    @Test
    public void contextLoads(a) {
        Helper.execute(new SupportBean(3)); }}Copy the code

The test results

A execute
Copy the code

extension

In Lambda expressions, the business implementation class is filtered, then the first business implementation class is retrieved and executed.

If there are multiple business implementation classes for filtering, and priorities are not determined, how do you extend this?

It’s as simple as defining a getPriority method in the Service interface

int getPriority(a);
Copy the code

The respective implementation classes then implement the corresponding getPriority method

You can then modify the Lambda expression and add the sorted method after filter to sort the business implementation classes

Service s = services.stream()
        .filter((service) -> service.isSupport(supportBean))
        .sorted(Comparator.comparing(Service::getPriority))
        .map(Optional::ofNullable)
        .findFirst()
        .flatMap(Function.identity())
        .orElse(null);
Copy the code

conclusion

The whole general framework has been basically built. If expansion is needed, only the corresponding business implementation classes need to be added, without modifying the code of other classes. Even the enumeration designed before can not be used, scalability greatly improved. To use it, you only need to modify the corresponding input parameter and corresponding name. If you have similar experience, welcome to join us and build together