Business complexity = if else? He first came to the Great God even used the tactics + factory and get rid of them!

For business development, the business logic complexity is inevitable, as the business development, the demand will become increasingly complex, in order to take into account a variety of situations, the code will inevitably be a lot of if-else.

Once too much code if-else, it will greatly affect its readability and maintainability.

First readability, it goes without saying, too many nested if-else code and will make reading the code difficult to understand in the end what it meant. Especially those of code without comments.

It followed maintainability, because particularly if-else, you want to add a new branch, you will be difficult to add, extremely susceptible to other branches.

I have seen a core application payments, the application needs to support a lot of business online payment functions, but each business has a lot of customization, so many of the core code has a large pile of if-else.

Each new business need to customize the time, see themselves if placed in front of the whole process, to ensure that their logic can be executed properly. This practice, the consequences can be imagined.

In fact, if-else is there a way you can eliminate, where the typical and widely used strategy is to make use of the models and the factory model, accurate to say that the use of these two design patterns of thought, the complete elimination of if-else code .

In this paper, we combine these two design patterns, how to eliminate if-else, and, also in conjunction with the Spring Framework and how, after reading this article so readers can immediately apply to their own projects.

This article comes to some of the code, but the authors try to make the content in simple examples and other forms of pseudo-code is not so boring.

Nausea if-else

Suppose we do a take-away platform, there is such a demand:

1, a take-away shop on the platform for promotional purposes, set a variety of member benefits, which includes 20% discount super affiliates, general membership discount 10% discount and ordinary users do not have three.

2, want the user when the payment, according to the user's membership level, you can know what kind of discount strategy in line with the user, and then discount, calculate the amount due.

3, along with business development, new demands require exclusive membership to shop at a single amount is greater than $ 30 when it can enjoy the benefits.

4, then, there is a metamorphosis of the demand, if the super-user membership has expired, and the expiration time in a week, then for single orders in accordance with the user's discount super affiliate, and strong reminders at the cash register guide the user to re-open membership, discount and once only.

So, we can see the following pseudo-code:

public BigDecimal calPrice(BigDecimal orderPrice, String buyerType) {    if (用户是专属会员) {        if (订单金额大于30元) {            returen 7折价格;        }    }    if (用户是超级会员) {        return 8折价格;    }    if (用户是普通会员) {        if(该用户超级会员刚过期并且尚未使用过临时折扣){            临时折扣使用次数更新();            returen 8折价格;        }        return 9折价格;    }    return 原价;}复制代码

The above is a demand for this period of price calculation logic, using pseudo-code is so complex, if it is really to write code, that complexity can be imagined.

This code, there are a lot of if-else, and there are a lot of nested if-else, whether it is the readability or maintainability are very low.

So, how to improve it?

Strategy Mode

Next, we try to introduce strategies to improve the model code maintainability and readability.

First, define an interface:

/** * @author mhcoding */public interface UserPayService {    /**     * 计算应付价格     */    public BigDecimal quote(BigDecimal orderPrice);}复制代码

Then define several policy categories:

/** * @author mhcoding */public class ParticularlyVipPayService implements UserPayService {    @Override    public BigDecimal quote(BigDecimal orderPrice) {         if (消费金额大于30元) {            return 7折价格;        }    }}public class SuperVipPayService implements UserPayService {    @Override    public BigDecimal quote(BigDecimal orderPrice) {        return 8折价格;    }}public class VipPayService implements UserPayService {    @Override    public BigDecimal quote(BigDecimal orderPrice) {        if(该用户超级会员刚过期并且尚未使用过临时折扣){            临时折扣使用次数更新();            returen 8折价格;        }        return 9折价格;    }}复制代码

After the introduction of the policy, we can calculate the price as follows:

/** * @author mhcoding */public class Test {    public static void main(String[] args) {        UserPayService strategy = new VipPayService();        BigDecimal quote = strategy.quote(300);        System.out.println("普通会员商品的最终价格为:" + quote.doubleValue());        strategy = new SuperVipPayService();        quote = strategy.quote(300);        System.out.println("超级会员商品的最终价格为:" + quote.doubleValue());    }}复制代码

The above is an example, the new policy may be different members of the class code, and then perform the corresponding method of calculating the price. This example knowledge and strategy mode, the reader is in the " How to explain to his girlfriend what is the strategy pattern? "A paper study.

But the real use in the code, such as using a web project, there is no way above the Demo directly.

First, in a web project, we create out of these above strategies Spring classes are being hosted, we do not own to a new instance of it.

Secondly, in a web project, if you really want to calculate the price, but also to know in advance the user's membership level, such as membership levels detected from the database, and then get a different strategy to perform the calculation method based on the price level.

So, web project to calculate the real price, then pseudo-code should look like this:

/** * @author mhcoding */public BigDecimal calPrice(BigDecimal orderPrice,User user) {     String vipType = user.getVipType();     if (vipType == 专属会员) {        //伪代码:从Spring中获取超级会员的策略对象        UserPayService strategy = Spring.getBean(ParticularlyVipPayService.class);        return strategy.quote(orderPrice);     }     if (vipType == 超级会员) {        UserPayService strategy = Spring.getBean(SuperVipPayService.class);        return strategy.quote(orderPrice);     }     if (vipType == 普通会员) {        UserPayService strategy = Spring.getBean(VipPayService.class);        return strategy.quote(orderPrice);     }     return 原价;}复制代码

Through the above code, we find that the code readability and maintainability seems to be better, but does not seem to reduce the if-else ah.

In fact, before the " how to explain to his girlfriend what is the strategy pattern? "Article, we described the advantages of the strategy pattern. However, the use of the Strategy pattern, or there is a relatively large disadvantages:

The client must know all the policy class, and the class to decide which strategy to use. This means that the client must understand the difference between these algorithms, in order to timely select the appropriate algorithm class.

In other words, although not if-else when calculating the price, but the choice of the specific policy or when the inevitable thing is to have some of the if-else.

In addition, the above pseudo-code, gets the policy object members from the Spring we are pseudo-code implementation, the code in the end of the corresponding Bean how to get it?

Next we look at how to use Spring and factory model, to solve these problems above.

Factory Pattern

To help us get UserPayService of each policy class from Spring, we create a factory class:

/** * @author mhcoding */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);    }}复制代码

This defines a UserPayServiceStrategyFactory the Map, to save all policy class instances and a method getByUserType may obtain an instance of the class corresponding to the type directly. There is also a register method, repeat this later.

With this factory after class, calculate the price code can be greatly optimized:

/** * @author mhcoding */public BigDecimal calPrice(BigDecimal orderPrice,User user) {     String vipType = user.getVipType();     UserPayService strategy = UserPayServiceStrategyFactory.getByUserType(vipType);     return strategy.quote(orderPrice);}复制代码

Above, the if-else is no longer necessary, and after the user get vip type, directly getByUserType factory method call it directly.

By strategy + Factory, a large degree of our code optimization, and greatly enhance the readability and maintainability.

However, the above also left a problem, that is used to save Map UserPayServiceStrategyFactory in all instances of the class strategy of how to be initialized? How the various strategies instance of an object to go into it?

Sign of Spring Bean

Remember our definition provided in the register UserPayServiceStrategyFactory way? He is to register service strategy.

Next, we wanted to approach the call register method, the Spring created out by IOC Bean registration go on the line.

This demand can borrow Spring seed InitializingBean interface provided, this interface provides treatment after property is initialized to Bean, it only includes afterPropertiesSet method, those who inherit the interface class, which will be executed after the initialization of the bean property.

So, we would like a little in front of each policy reform can:

/** * @author mhcoding */@Servicepublic class ParticularlyVipPayService implements UserPayService,InitializingBean {    @Override    public BigDecimal quote(BigDecimal orderPrice) {         if (消费金额大于30元) {            return 7折价格;        }    }    @Override    public void afterPropertiesSet() throws Exception {        UserPayServiceStrategyFactory.register("ParticularlyVip",this);    }}@Servicepublic class SuperVipPayService implements UserPayService ,InitializingBean{    @Override    public BigDecimal quote(BigDecimal orderPrice) {        return 8折价格;    }    @Override    public void afterPropertiesSet() throws Exception {        UserPayServiceStrategyFactory.register("SuperVip",this);    }}@Service  public class VipPayService implements UserPayService,InitializingBean {    @Override    public BigDecimal quote(BigDecimal orderPrice) {        if(该用户超级会员刚过期并且尚未使用过临时折扣){            临时折扣使用次数更新();            returen 8折价格;        }        return 9折价格;    }    @Override    public void afterPropertiesSet() throws Exception {        UserPayServiceStrategyFactory.register("Vip",this);    }}复制代码

Only need to implement a strategy for each class of service are achieved InitializingBean interface and implement its afterPropertiesSet method, you can call UserPayServiceStrategyFactory.register in this method.

In this way, when the Spring initialized when created VipPayService, SuperVipPayService and ParticularlyVipPayService, the property will be after the initialization Bean, Bean registered to UserPayServiceStrategyFactory put this in.

The above code, in fact, there are some duplicate code, there can also be introduced to further streamline the template method pattern here is not started.

There is, when UserPayServiceStrategyFactory.register called, the first argument needs to pass a string, then this fact can be optimized away. Such as the use enumeration, or a custom policy getUserType method in each class, each can be implemented.

to sum up

In this paper, we adopted the strategy pattern, factory pattern and the Spring of InitializingBean, improved code readability and maintainability, wipe the cook if-else.

The text of this approach, we can try it immediately, this practice is often used in our daily development, but also the many derivatives usage, they are also very easy to use. We have the opportunity to re-introduce later.

In fact, if the readers to understand the strategy pattern and factory pattern, then, it is not used in the strict sense of the text above strategy pattern and factory pattern.

First, the policy mode important role in Context there is no, there is no Context, also in combination did not use, but the use of the plant instead.

In addition, there's really just UserPayServiceStrategyFactory maintains a Map, and provides a method register and get it, but actually help the factory pattern to create objects, and is not used here.

So, readers do not have to tangle with in the end is not really a strategy pattern and factory pattern. Moreover, there is also a longer extension, called GOF 23 kinds of design patterns, no matter where or which blog to see the book, code samples are simple, but many of them are based on our daily development frameworks like Spring, there is no way direct use.

So, for learning design patterns, it is important to learn their thinking, instead of code to achieve! ! !

If readers are interested, you can follow the best practices to use more design patterns and frameworks such as Spring combination. We hope that through this article, the reader can really use design patterns in your code.



Guess you like

Origin juejin.im/post/5dad23685188251d2c4ea2b6