Case demonstration of strategic model of behavior model

Posted by madwormer2 on Sat, 08 Jan 2022 13:07:17 +0100

1. Introduction to strategy mode

Strategy pattern is a behavior pattern and a sharp tool to replace a large number of ifelse. The scenarios it can solve are generally those with similar replaceable behavioral logic algorithms. For example, different types of transactions (credit cards, Alipay, WeChat), the unique ID strategy (UUID, DB self increment, DB+Redis, snowflake algorithm, Leaf algorithm) can be packaged by policy mode and supplied to external use.

The strategy model is a bit like the golden bag given by Zhuge Liang to Liu Guanzhang in the romance of the Three Kingdoms:

  • The first brocade bag: meet Qiao Guolao and make Liu Bei's marriage known to all the people of Soochow.
  • The second brocade bag: use Cao Cao's lie of beating Jingzhou to deceive Liu Bei who ran away in gentle township to go back.
  • The third brocade bag: let Mrs. sun settle the pursuit of Soochow. She is Sun Quan's sister. Soochow generals are afraid of her.

2. Case scenario simulation

In this case, we simulate various types of coupons used in shopping (full reduction, direct reduction, discount, n yuan purchase).

This scenario is almost a money saving channel for daily shopping. We hope to find some coupons to make the purchased goods more affordable. In addition, there will be more coupons to calculate which goods to buy together for more discount.

This scenario is quite cool for users, but with the continuous iteration of the product, it is not easy for programmers to develop. Because there are many rules and preferential logic, we simulate one of the ways to calculate the preferential, and use the policy mode to implement it.

3. Implement with a lump of code

The design of coupons may be very simple at first, that is, a discount of one amount, and there are not so many types as now. Therefore, if there is no experience in such scenes, the design is often very simple. However, with the continuous iteration of product functions, if the program originally designed does not have good scalability, it will become more and more chaotic in the future.

engineering structure

org.itstack.demo.design
----CouponDiscountService.java
  • The structure of a Tuo project is very simple, and it is also the most direct process oriented development method.

code implementation

public class CouponDiscountService {
    public double discountAmount(int type, double typeContent, double skuPrice, double typeExt) {
    // 1. Direct reduction of bonds
    if (1 == type) {
    	return skuPrice - typeContent;
    }
    // 2. Full coupon reduction
    if (2 == type) {
        if (skuPrice < typeExt) return skuPrice;
        return skuPrice - typeContent;
    }
    // 3. Discount coupon
    if (3 == type) {
    	return skuPrice * typeContent;
    }
    // 4. n yuan purchase
    if (4 == type) {
    	return typeContent;
    }
    return 0D;
    }
}
  • The input parameters include: coupon type, coupon amount typeContent and commodity amount skuPrice. Because some coupons are full or reduced, typeExt is added, which is also a problem of poor extension of the method.
  • Finally, the implementation of the coupon deduction amount in the whole method body may be the simplest coupon at first. Later, with the increase of product functions, the if statement is continuously expanded, and the actual code is much more than this.

4. Policy pattern refactoring code

Compared with the above process oriented development, design patterns will be used here to optimize the code structure and enhance the scalability of sorting.

engineering structure

org.itstack.demo.design
----event
	----MJCouponDiscount.java
	----NYGCouponDiscount.java
	----ZJCouponDiscount.java
	----ZKCouponDiscount.java
----Context.java
----ICouponDiscount.java

Policy pattern model structure

  • The overall structure model is not complex, which mainly reflects the different calculation strategies of different types of coupons in the way of calculating coupons.
  • This includes an interface class ICouponDiscount and the implementation methods of four coupon types.
  • Finally, the upper and lower control classes of the policy mode are provided to handle the overall policy service.

code implementation

Coupon interface

public interface ICouponDiscount<T> {
    /**
    * Coupon amount calculation
    * @param couponInfo Coupon discount information: direct reduction, full reduction, discount, N yuan purchase
    * @param skuPrice sku amount of money
    * @return Amount after discount
    */
    BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice);
}
  • The coupon discount interface is defined, and generics are added for different types of interfaces, which can pass different type parameters.
  • The interface contains the commodity amount and the amount returned after the final discount. In actual development, there will be more interface parameters, but these are the core logic.

Full minus interface implementation

public class MJCouponDiscount implements ICouponDiscount<Map<String,String>> {
    /**
    * Full minus calculation
    * 1. It is judged that - n yuan after x Yuan is satisfied
    * 2. The minimum payment amount is 1 yuan
    */
    public BigDecimal discountAmount(Map<String,String> couponInfo,BigDecimal skuPrice) {
        String x = couponInfo.get("x");
        String o = couponInfo.get("n");
        // If the amount is less than the condition amount, the original price will be returned directly
        if (skuPrice.compareTo(new BigDecimal(x)) < 0) return skuPrice;
        // Less preferential amount judgment
        BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(o));
        if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
        return discountAmount;
    }
}

Direct reduction interface implementation

public class ZJCouponDiscount implements ICouponDiscount<Double> {
    /**
    * Direct reduction calculation
    * 1. Commodity price - preferential price
    * 2. The minimum payment amount is 1 yuan
    */
    public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
        BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(couponInfo));
        if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
        return discountAmount;
    }
}

Discount interface implementation

public class ZKCouponDiscount implements ICouponDiscount<Double> {
    /**
    * Discount calculation
    * 1. Commodity price X discount ratio
    * 2. Keep two decimal places
    * 3. The minimum payment amount is 1 yuan
    */
    public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
        BigDecimal discountAmount = skuPrice.multiply(new BigDecimal(couponInfo)).setScale(2, BigDecimal.ROUND_HALF_UP);
        if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
        return discountAmount;
    }
}

N yuan purchase

public class NYGCouponDiscount implements ICouponDiscount<Double> {
    /**
    * n Yuan purchase calculation
    * 1. Purchase for a fixed amount regardless of the original price
    */
    public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
    	return new BigDecimal(couponInfo);
    }
}
  • The above are the strategies for calculating discount amount for four different types of coupons.

Policy control class

public class Context<T> {
    private ICouponDiscount<T> couponDiscount;
    
    public Context(ICouponDiscount<T> couponDiscount) {
    	this.couponDiscount = couponDiscount;
    }
    public BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice) {
    	return couponDiscount.discountAmount(couponInfo, skuPrice);
    }
}
  • The control class of the policy mode is mainly that different policy implementations can be passed externally, and preferential policy calculation can be performed through a unified method.
  • In addition, it can also be wrapped into a map structure, so that the external only needs the corresponding generic type to use the corresponding service.

Test verification

Writing test classes

@Test
public void test_zj() {
    // Direct reduction: 100-10
    Context<Double> context = new Context<Double>(new ZJCouponDiscount());
    BigDecimal discountAmount = context.discountAmount(10D, new BigDecimal(100));
    logger.info("Test result: amount after direct reduction {}", discountAmount);
    
    // Full minus: full 100-10
    Context<Map<String,String>> context = new Context<Map<String,String>> (new MJCouponDiscount());
    Map<String,String> mapReq = new HashMap<String, String>();
    mapReq.put("x","100");
    mapReq.put("n","10");
    BigDecimal discountAmount = context.discountAmount(mapReq, new BigDecimal(100));
    logger.info("Test result: amount after full discount {}", discountAmount);
    
    // 10% discount
    Context<Double> context = new Context<Double>(new ZKCouponDiscount());
    BigDecimal discountAmount = context.discountAmount(0.9D, new BigDecimal(100));
    logger.info("Test result: amount after 10% discount {}", discountAmount);
    
    // n yuan purchase
    Context<Double> context = new Context<Double>(new NYGCouponDiscount());
    BigDecimal discountAmount = context.discountAmount(90D, new BigDecimal(100));
    logger.info("test result:n Amount after RMB purchase discount {}", discountAmount);
}

test result

15:43:22.035 [main] INFO org.itstack.demo.design.test.ApiTest - Test result: 90% after direct reduction
15:43:42.695 [main] INFO org.itstack.demo.design.test.ApiTest - Test result: 90% after full discount
15:44:05.602 [main] INFO org.itstack.demo.design.test.ApiTest - Test result: 90% after 10% discount.00
15:44:24.700 [main] INFO org.itstack.demo.design.test.ApiTest - test result:n 90 yuan after purchase discount    
    
Process finished with exit code 0
  • The above verifies the preferential strategies of different types of coupons, and the test results meet our expectations.

5. Summary

  • The above strategy model cases are relatively uncomplicated. The main logic is reflected in the discount calculation strategies for different types of coupons. The structure is also relatively simple, and such a design pattern is also very common in practical development. In addition, the structure of this design is similar to that of command mode and adapter mode, but the idea is different.
  • Through the use of policy pattern, we can optimize the if statements in our method to meet the isolation and scalability in the design principles.
  • Policy mode, adapter mode and combination mode are similar in some structures, but each mode has its own logical characteristics and needs to be realized slowly in the follow-up process.

Topics: Java Design Pattern