Coding With Fun
Home Docker Django Node.js Articles FAQ

Using Spring to implement policy patterns could have been as simple as that!


May 31, 2021 Article blog



This article comes from Python Geek Technology Author: Duck Blood Fans

Recently, I learned a little trick when I looked at my colleagues' code, which is very useful in some scenarios, and I'll share it with you here.

Spring @Autowired annotations, we should be no strangers, with Spring's affirmation can not be separated from this annotation, through this annotation can help us automatically inject the beans we want.

In addition to this basic feature, @Autowired has more powerful features, as well as injecting arrays of specified types, List/Set collections, and even Map objects.

For example, the current application has a payment interface PayService respectively, need to connect Alipay, WeChat payment, bank cards, so there are three different implementation AliPayService WechatPayservice BankCardPayService

The relationship between the four classes is shown in the following image:

 Using Spring to implement policy patterns could have been as simple as that!1

If at this point I need to get all PayService beans for the current system class, the old way we can only get them through BeanFactory or ApplicationContex t.

// 首先通过 getBeanNamesForType 获取 PayService 类型所有的 Bean
String[] names = ctx.getBeanNamesForType(PayService.class);
List anotherPayService = Lists.newArrayList();
for (String beanName : names) {
    anotherPayService.add(ctx.getBean(beanName, PayService.class));
}
// 或者通过 getBeansOfType 获取所有 PayService 类型
Map beansOfType = ctx.getBeansOfType(PayService.class);
for (Map.Entry entry : beansOfType.entrySet()) {
    anotherPayService.add(entry.getValue());
}

But now that we don't have to worry about it, we can inject PayService Bean array directly using @Autowired or PayService List/Set collection, or even PayService Map collection.

@Autowired
List payServices;


@Autowired
PayService[] payServicesArray;

 Using Spring to implement policy patterns could have been as simple as that!2

Knowing this feature, it's easy when we need to implement a policy pattern with Spring.

There may be small partners do not know much about the policy model, it doesn't matter, this kind of powder introduces a business scenario, through this scenario to introduce the policy model.

Or the example above, our current system needs to connect WeChat payment, Alipay, and bank card payment.

When we receive this request, we first need to get the corresponding interface documentation, analysis of the commonality of the three.

Let's say we find here that the three patterns are similar, but some of them are different.

So we abstract a set of common PayService based on the commonality of the three,

public interface PayService {
    PayResult epay(PayRequest request);
}

Then implement three implementation classes, each inheriting the interface.

So now the problem is, because there are three implementation classes, how to choose the specific implementation class?

In fact, this problem is very good to solve, the request parameters pass in a unique identity, and then we select the appropriate implementation class according to the identity.

Let's say we're asking for a channelNo field in the request class PayRequest which represents the unique identification of the corresponding payment channel, such as Alipay is: 00000001, WeChat pays 0000002, and the card pays 0000003.

Then we need to map the unique identity one by one with the concrete implementation class, just as we can use Map store this mapping relationship.

We implement a RouteService and the code logic is as follows:

@Service
public class RouteService {


    @Autowired
    Map payServiceMap;


    public PayResult epay(PayRequest payRequest) {
        PayService payService = payServiceMap.get(payRequest.getChannelNo());
        return  payService.epay(payRequest);
    }


}

We automatically inject all the related beans of PayService and then use a unique identity to find the implementation class. RouteService

This allows us to mask differences in payment channels, and other service classes simply call RouteService

However, this implementation is still a bit of a problem, because we uniquely identify as a string of numbers, if like we above directly use @Autowired injection Map which requires us to implement the Bean name of the class is 00000001 these.

But such a name is not very elegant, so that later students will be difficult to understand, not good maintenance.

So we need to make a transition, and we can do that.

First, let's modify the PayService interface to add a method through which each concrete implementation class returns its unique identity.

public interface PayService {


    PayResult epay(PayRequest request);


    String channel();
}

Specifically, the code of an Alipay implementation class is specified, and other implementation class implementations are similar.

@Service("aliPayService")
public class AliPayService implements PayService {


    @Override
    public PayResult epay(PayRequest request) {
        // 业务逻辑
        return new PayResult();
    }
    @Override
    public String channel() {
        return "00000001";
    }
}

Finally, let's revamp RouteService with the following logic:

@Service
public class RouteService {


    @Autowired
    Set payServiceSet;

    
    Map payServiceMap;


    public PayResult epay(PayRequest payRequest) {
        PayService payService = payServiceMap.get(payRequest.getChannelNo());
        return  payService.epay(payRequest);
    }


    @PostConstruct
    public void init() {
        for (PayService payService : payServiceSet) {
            payServiceMap = new HashMap();
            payServiceMap.put(payService.channel(), payService);
        }
    }
}

The code above first automatically injects a collection of PayService and then we turn it into a Map so that the internal storage happens to be the only identity that maps to the implementation class.

That's W3Cschool编程狮 said about implementing policy patterns with Spring could have been as simple as that! Related to the introduction, I hope to help you.