“This is my 26th day of the August Genwen Challenge.More challenges in August”

What is strategic mode?

Strategy Pattern, also known as Policy Pattern, encapsulates the defined algorithm family separately so that they can be replaced with each other, so that the change of the algorithm will not affect the users who use the algorithm. It belongs to the behavioral pattern.

The strategy pattern uses object-oriented inheritance and polymorphism mechanisms to achieve different implementations of the same behavior in different scenarios.

Second, the application scenario of policy mode

Policy patterns are also widely used in life scenarios. For example, a person’s tax rate is related to his salary, and different salary levels correspond to different tax rates. For example, in the context of Internet mobile payment, we need to choose the payment method after each order before payment.

The strategy pattern solves the problem of using if… The else or switch… The complexity and bloatiness that case brings. In daily business development, the policy pattern applies to the following scenarios:

  1. There are multiple approaches to the same type of problem, each of which can solve the problem independently.
  2. Scenarios where the algorithm needs to switch freely.
  3. Scenarios where algorithm rules need to be masked.

Let’s start with a common UML class diagram for the policy pattern:

From the UML class diagram, we can see that the policy pattern consists of three main roles:

  1. Context: The Context used to manipulate policies, shield high-level module (client) policies, direct access to algorithms, and encapsulate possible changes.
  2. Abstract Policy role (Strategy) : Specifies the behavior of a policy or algorithm.
  3. ConcreteStrategy roles: concrete policy or algorithm implementations.

Note: The Context in the policy pattern is supposed to decouple the client from the policy class, allowing the client to communicate fully with the Context without having to relate to specific policies.

Third, use strategy mode to achieve promotion and discount business scenarios

As we all know, e-commerce platforms often have preferential activities. There are many kinds of preferential strategies, such as receiving coupons for deduction, cash back promotion and group discount. To simulate this, we create an abstract PromotionStrategy for a PromotionStrategy:

public interface IPromotionStrategy {

    void promotion(a);
}
Copy the code

Then create the following categories: CouponStrategy, CashBackStrategy, GroupBuyStrategy and EmptyStrategy:

CouponStratey class:

public class CouponStrategy implements IPromotionStrategy {
    @Override
    public void promotion(a) {
        System.out.println("Use coupons to offset"); }}Copy the code

CashBackStrategy class:

public class CashBackStrategy implements IPromotionStrategy {
    @Override
    public void promotion(a) {
        System.out.println("Cash back, direct payment to alipay account"); }}Copy the code

GroupBuyStrategy class:

public class GroupBuyStrategy implements IPromotionStrategy {
    @Override
    public void promotion(a) {
        System.out.println("Discount for a group of five."); }}Copy the code

EmptyStrategy class:

public class EmptyStrategy implements IPromotionStrategy {
    @Override
    public void promotion(a) {
        System.out.println("No discount"); }}Copy the code

Then create the promotional activity scheme PromotionActivity class:

public class PromotionActivity {

    private IPromotionStrategy strategy;

    public PromotionActivity(IPromotionStrategy strategy) {
        this.strategy = strategy;
    }

    public void execute(a) { strategy.promotion(); }}Copy the code

Write a client test class:

public class Test {

    public static void main(String[] args) {
        PromotionActivity activity618 = new PromotionActivity(new CouponStrategy());
        PromotionActivity activity1111 = new PromotionActivity(newCashBackStrategy()); activity618.execute(); activity1111.execute(); }}Copy the code

At this point, you may find that the above test code is not practical if you put it into a real business scenario. Because when we do activities, we often need to dynamically choose promotion strategies according to different needs, and will not implement multiple offers at one time. So, our code (simple simulation ha!!) It usually goes something like this:

public void test(a) {
        PromotionActivity activity = null;
        String promotionKey = "COUPON";

        if ("COUPON".equalsIgnoreCase(promotionKey)) {
            activity = new PromotionActivity(new CouponStrategy());
        } else if ("CASHBACK".equalsIgnoreCase(promotionKey)) {
            activity = new PromotionActivity(new CashBackStrategy());
        }
        / /...
        activity.execute();
    }
Copy the code

After this transformation, to meet the business needs, customers can choose different preferential strategies according to their own needs. However, after a period of business accumulation, we will have more and more promotional activities. As a result, our programming monkey brother is very busy, before each activity will have to change the code overnight, and repeated tests, judgment logic may become more and more complex. At this point, do we need to think about code refactoring? Looking back at the design patterns we’ve learned, how can we optimize this code? In fact, we can combine the singleton pattern with the factory pattern.

Simple simulation ha, have more advanced writing method!!

Create the PromotionStrategyFactory class:

public class PromotionStrategyFactory {

    private PromotionStrategyFactory(a) {}private static final Map<String, IPromotionStrategy> PROMOTIONS = new HashMap<>();
    private static final IPromotionStrategy EMPTY = new EmptyStrategy();

    static {
        PROMOTIONS.put(PromotionKey.COUPON, new CouponStrategy());
        PROMOTIONS.put(PromotionKey.CASHBACK, new CashBackStrategy());
        PROMOTIONS.put(PromotionKey.GROUPBUY, new GroupBuyStrategy());
    }

    public static IPromotionStrategy getPromotionStrategy(String promotionKey) {
        IPromotionStrategy strategy = PROMOTIONS.get(promotionKey);
        return strategy == null ? EMPTY : strategy;
    }

    private interface PromotionKey {
        String COUPON = "COUPON";
        String CASHBACK = "CASHBACK";
        String GROUPBUY = "GROUPBUY";
    }

    public static Set<String> getPromotionKeys(a) {
        returnPROMOTIONS.keySet(); }}Copy the code

The client code should look like this:

public class Test {

    public static void main(String[] args) {
        String promotionKey = "COUPON"; IPromotionStrategy strategy = PromotionStrategyFactory.getPromotionStrategy(promotionKey); strategy.promotion(); }}Copy the code

After code optimization, is it easy to maintain our program monkey brother? Each new activity does not affect the original code logic.

Implement the business scenario of choosing payment mode with policy mode

To deepen our understanding of the strategic pattern, let’s take another example. I believe that you have used Alipay, wechat Pay, UnionPay and JINGdong Paiao. A common application scenario is that users will be prompted to select a payment method when placing an order and paying. If users do not select the payment method, the system will choose the recommended payment method by default for settlement.

Create the Payment abstract class to define the Payment specification and Payment logic as follows:

/** * payment channels */
public abstract class Payment {

    public abstract String getName(a);

    // General-purpose logic is implemented in abstract classes
    public MsgResult pay(String uid, double amount) {
        // Is the balance sufficient
        if (queryBalance(uid) < amount) {
            return new MsgResult(500."Payment failure"."Insufficient balance");
        }
        return new MsgResult(200."Payment successful"."Amount paid" + amount);
    }

    protected abstract double queryBalance(String uid);
}
Copy the code

Create specific payment methods respectively, AliPay category:

public class AliPay extends Payment {
    @Override
    public String getName(a) {
        return "Alipay";
    }

    @Override
    protected double queryBalance(String uid) {
        return 900; }}Copy the code

Jd Baitiao JDPay class:

public class JDPay extends Payment {
    @Override
    public String getName(a) {
        return Jingdong IOUS;
    }

    @Override
    protected double queryBalance(String uid) {
        return 500; }}Copy the code

WeChatPay class:

public class WeChatPay extends Payment {
    @Override
    public String getName(a) {
        return "Wechat Pay";
    }

    @Override
    protected double queryBalance(String uid) {
        return 200; }}Copy the code

UnionPay class:

public class UnionPay extends Payment {
    @Override
    public String getName(a) {
        return "Union Pay";
    }

    @Override
    protected double queryBalance(String uid) {
        return 120; }}Copy the code

Create a wrapper class MsgResult for payment status:

/** * The status after payment is completed */
@Data
public class MsgResult {

    private int code;
    private Object data;
    private String msg;

    public MsgResult(int code, Object data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg; }}Copy the code

Create payment Management Policy class:

/** * Payment policy management */
public class PayStrategy {

    public static final String ALI_PAY = "AliPay";
    public static final String JD_PAY = "JdPay";
    public static final String WECHAT_PAY = "WeChatPay";
    public static final String UNION_PAY = "UnionPay";
    public static final String DEFAULT_PAY = ALI_PAY;

    private static final Map<String, Payment> STRATEGY = new HashMap<>();

    static {
        STRATEGY.put(ALI_PAY, new AliPay());
        STRATEGY.put(JD_PAY, new JDPay());
        STRATEGY.put(WECHAT_PAY, new WeChatPay());
        STRATEGY.put(UNION_PAY, new UnionPay());
    }

    public static Payment get(String payKey) {
        if(! STRATEGY.containsKey(payKey)) {return STRATEGY.get(DEFAULT_PAY);
        }
        returnSTRATEGY.get(payKey); }}Copy the code

Create Order Order class:

@Data
@AllArgsConstructor
public class Order {

    private String uid;
    private String orderId;
    private double amount;

    public MsgResult pay(a) {
        return pay(PayStrategy.DEFAULT_PAY);
    }

    public MsgResult pay(String payKey) {
        Payment payment = PayStrategy.get(payKey);
        System.out.println("Welcome to use" + payment.getName());
        System.out.println("The transaction amount is:" + amount + ", start the deduction.");
        returnpayment.pay(uid, amount); }}Copy the code

Test code:

public class Test {

    public static void main(String[] args) {
        Order order = new Order("1"."111".Awesome!); System.out.println(order.pay(PayStrategy.DEFAULT_PAY)); }}Copy the code

Running results:

At this point, does the partner have a deeper understanding of how the strategic pattern is used?

Five, the strategy model in the framework of the source code

Compare () {compare();} compare();

public interface Comparator<T> {...int compare(T o1, T o2); . }Copy the code

In a very classic scenario, Spring also uses a policy pattern for initialization, with different classes using different initialization strategies. Let’s look at the source code for InstantiationStrategy:

public interface InstantiationStrategy {
    Object instantiate(RootBeanDefinition var1, @Nullable String var2, BeanFactory var3) throws BeansException;

    Object instantiate(RootBeanDefinition var1, @NullableString var2, BeanFactory var3, Constructor<? > var4, Object... var5) throws BeansException;

    Object instantiate(RootBeanDefinition var1, @Nullable String var2, BeanFactory var3, @Nullable Object var4, Method var5, Object... var6) throws BeansException;
}
Copy the code

The top strategy abstract is very simple, but it has two strategies under SimpleInstantiationStrategy and CglibSubclassingInstantiationStrategy, we look at the class diagram:

From class diagram we also found that CglibSubclassingInstantiationStrategy strategy class inherits SimpleInstantiationStrategy class, between multiple strategies in practice are still can continue to use. Partners can be used as a reference and can be designed as required in actual business scenarios.

Advantages and disadvantages of strategic mode

Advantages:

  1. The policy pattern conforms to the open close principle.
  2. Avoid multiple conditional statements such as if… else… Statement, switch statement.
  3. Using the policy pattern can improve the confidentiality and security of the algorithm.

Disadvantages:

  1. The client must know all the policies and decide for itself which policy class to use.
  2. There are many policy classes in the code, which makes maintenance difficult.

7. Friendship links

Design Patterns – Factory Patterns learning tour

Design Patterns – a learning tour of singleton patterns

Design Patterns – A learning journey of prototyping patterns

Design Patterns – Builder patterns learning tour

Design Patterns – Agent patterns learning tour

Design Patterns – A learning tour of facade patterns

Design Patterns – A learning tour of decorator patterns

Design Patterns – Enjoy yuan patterns learning journey

Design Patterns – A learning journey of composite patterns

Design Patterns – Adapter patterns learning journey

Design Patterns – Bridge patterns learning journey

Design Patterns – Delegation patterns learning journey

Design Patterns – a template method pattern learning journey

Welcome to follow the wechat public account (MarkZoe) to learn from and communicate with each other.