preface

Recently, I received a demand: to develop an aggregative payment service, which provides a unified interface for other internal projects to realize the payment ability initiation of different payment platforms, such as Alipay, wechat, UnionPay and so on. For dealing with similar payment operation platform specific implementation different situation, to make each platform interface ability can be independent, and convenient to extend the subsequent new payment platform, I introduced the design pattern strategy to cope with the demand of the scene, to take this study summarizes the strategy pattern, and also have the purpose of this article, hope to help in learning strategy mode of students.

Why do you need a strategic pattern

Familiar snippets of code come up in our daily work:

if(condition1){
 // do something1
} else if (condition2){
 // do something2
} else if (condition3){
 // do something3
}
Copy the code

In each if condition, there are dozens or even hundreds of lines of business processing, which are independent of each other and have the same purpose, all converging in one method. Not only does this make the class bloated and verbose, but the different logic is changed in a single class, which is difficult to maintain and extend. So what can be done to optimize this large chunk of code to make it more flexible and maintainable while still doing the job?

To solve this problem, the protagonist of this article – the strategy pattern comes on stage, as a relatively simple behavior pattern in the design pattern, in fact, many frameworks see it in the figure, we will later identify the application of the strategy pattern from the source code of each framework. Using the policy pattern helps us to encapsulate each processing logic into a separate class, using the corresponding class for the processing logic that the client class needs to do, and calling its methods that encapsulate the details of the business processing. In this way, the client class reduces the amount of code required for business processing logic and makes itself more streamlined. When the business logic is changed, it should be changed in the corresponding class without affecting other classes. And if there is a new business logic, just add a similar class for implementation, for the client class to call.

What are strategic patterns

Let’s take a look at the definition and composition of a policy pattern and its basic form.

Let’s take a look at the wikipedia definition of a policy pattern:

In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.

Policy mode, also called policy mode, allows a program to choose an algorithm to execute at run time. There is usually a class of algorithm implementations that provide external alternative execution. The algorithm here, also called policy, corresponds to the concrete processing logic mentioned in the previous section.

Let’s take a look at the definition of strategic patterns in Design Patterns: The Foundation of Reusable Object-oriented Software:

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from the clients that use it.

To interpret its definition again: define a class of algorithms, each independent encapsulation implementation, and is replaceable with each other. Beyond that, it’s up to the client class to decide which algorithm to use.

Both definitions mention the term algorithm, which refers to the complete, non-detachable processing of business logic. Generally, interfaces or abstract classes are used to represent the abstraction of a class of algorithms, and various operations are provided to realize the class of algorithms, thus forming a class of independent and replaceable algorithms, also called policy groups.

With this definition in mind, let’s look at the common class diagram of the policy pattern:

Three types of roles are involved in the class diagram: Context, Strategy, and ConcreteStrategy

  • Strategy: An abstract policy role that represents an algorithm’s interface or abstract class and defines the methods and attributes that each algorithm or policy needs to have.
  • Context: a Context role that refers to a policy interface object and blocks direct access to policies or methods by external modules. Only methods provided by Context can be accessed.
  • ConcreteStrategy: A concrete implementation of an abstract strategy that contains a concrete algorithm, usually with more than one implementation and multiple classes.

The functions and responsibilities of these three roles are very clear, the corresponding source code implementation is also very simple, now we will take a quick look at the corresponding universal source code for each role.

// Abstract policy roles
public interface Strategy {
    void doSomething(a);
}

// Specify the policy role
public class ConcreteStrategy implements Strategy {
    @Override
    public void doSomething(a) {
        System.out.println("ConcreteStrategy doSomething !"); }}// Context role
public class Context {
    private final Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void doAnything(a) {
        this.strategy.doSomething(); }}Copy the code

With the basic code structure of the policy pattern, it is very simple to use in the client class. If you want a policy, it generates its specific policy object into the context object, and then the context object performs the specific policy operation. The code is as follows:

public class Client {
    public static void main(String[] args) {
        Strategy strategy = new ConcreteStrategy();
        Context context = new Context(strategy);
        context.doAnything(); // ConcreteStrategy doSomething !}}Copy the code

Identifying strategy patterns

After looking at the definition, role composition, and common code structure of the policy pattern, let’s take a look at the application of the policy pattern in the common framework to deepen the understanding of the policy pattern.

JDK and policy patterns

In common Java collections frameworks, the comparator Java.util.parator is designed using the policy pattern. Comparator is an abstract policy interface. As long as there’s a class that implements compare, it customizes the compare method, and that class becomes a concrete policy class. You can find implementations of this abstract policy interface in many places. Java.util.parators also provides NaturalOrderComparator and NullComparator policy classes. The Java.util. Collections class, which uses the Comparator, is the Context role and provides the collection comparison function as a static method.

Spring Framework and Policy patterns

The Spring framework was first known for its IoC and DI features, which do not require developers to create objects themselves, but identify and instantiate them through the Spring IoC container. Will perform create instances of objects in the Spring the operating encapsulation for an algorithm, using interface class org. Springframework. Beans. Factory. Support. InstantiationStrategy statement, And specific strategy class have org. Springframework. Beans. Factory. Support. SimpleInstantiationStrategy and Org. Springframework. Beans. Factory. Support. CglibSubclassingInstantiationStrategy two, And is CglibSubclassingInstantiationStrategy inheritance of SimpleInstantiationStrategy extension, Is truly the strategy used in the Spring container classes, specific application source code can refer to the org. Springframework. Beans. Factory. Support. AbstractAutowireCapableBeanFactory classes:

/**
    * Instantiate the given bean using its default constructor.
    * @param beanName the name of the bean
    * @param mbd the bean definition for the bean
    * @return a BeanWrapper for the new instance
    */
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
    / /...
    beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
    / /...
}
Copy the code

How to use the policy pattern

Application instance

As the saying goes, we should think about how to use the strategy mode in our daily development projects after contacting it. Here is a simple example to illustrate the way of using the strategy mode. Suppose you have a requirement to implement two different decompression methods for a directory or file: zip and gzip, and other decompression methods may be added later.

We first abstract the decompression algorithm into an abstract strategy interface, CompressStrategy, and provide compression methods compress and uncompress, which accept the source file path and destination file path respectively.

Policy classes are usually named with Strategy as the suffix to indicate that they adopt the Strategy mode to design, so as to simplify the cost of communicating with others.

public interface CompressStrategy {
    public boolean compress(String source, String to);
    public boolean uncompress(String source, String to);
}
Copy the code

Then the abstract policy interface is implemented to provide zip compression algorithm and GZIP compression algorithm respectively. The code is as follows:

public class ZipStrategy implements CompressStrategy {

    @Override
    public boolean compress(String source, String to) {
        System.out.println(source + "-- >" + to + "ZIP compression succeeded!");
        return true;
    }

    @Override
    public boolean uncompress(String source, String to) {
        System.out.println(source + "-- >" + to + "ZIP decompressed successfully!");
        return true; }}public class GzipStrategy implements CompressStrategy {

    @Override
    public boolean compress(String source, String to) {
        System.out.println(source + "-- >" + to + "GZIP compression succeeded!");
        return true;
    }

    @Override
    public boolean uncompress(String source, String to) {
        System.out.println(source + "-- >" + to + "GZIP unzipped successfully!);
        return true; }}Copy the code

In order to simplify the implementation of the code sample is just a simple printing operation, the specific implementation can refer to the JDK API for operation.

Let’s look at the Context role code implementation:

public class CompressContext {

    private CompressStrategy compressStrategy;

    public CompressContext(CompressStrategy compressStrategy) {
        this.compressStrategy = compressStrategy;
    }

    public boolean compress(String source, String to) {
        return compressStrategy.compress(source, to);
    }

    public boolean uncompress(String source, String to) {
        returncompressStrategy.uncompress(source, to); }}Copy the code

It’s very simple, you just pass in a specific algorithm, execute it, and at this point the standard strategy pattern is written. The client class simply passes the specified compression policy object to the CompressContext object as needed. If you want to add a compression algorithm, you simply provide a new implementation of the CompressStrategy interface that can be passed to the CompressContext object.

public class Client {
    public static void main(String[] args) {
        CompressContext context;
        System.out.println("======== Execution algorithm ========");
        context = new CompressContext(new ZipStrategy());
        context.compress("c:\\file"."d:\\file.zip");
        context.uncompress("c:\\file.zip"."d:\\file");
        System.out.println("======== switch algorithm ========");
        context = new CompressContext(new GzipStrategy());
        context.compress("c:\\file"."d:\\file.gzip");
        context.uncompress("c:\\file.gzip"."d:\\file"); }}Copy the code

Application example of the above strategy pattern is very simple, there are also many similar applications, such as docking third-party payment, different platforms have different payment apis, this API operation interface can be abstracted as strategy, client a specific platform payment interface, we need to call specific pay strategy implementation, And each payment policy class is independent and replaceable.

Applicable scenario

At the end of this section, the applicable scenarios of the policy mode are summarized as follows:

  • If an object has many behaviors, they serve the same purpose, and these behaviors are implemented using multiple conditional selection statements.

  • When a system needs to switch algorithms dynamically, it selects an algorithm to execute.

  • The client class does not need to know the implementation details of the specific algorithm, just invoke and fulfill the required requirements.

Lambda and the policy pattern

After JDK 8, Lambda provides a more streamlined implementation of the policy pattern. If the policy interface is a function interface, it does not need to declare new classes to implement different policies. Instead, it can be implemented by passing Lambda and is much more concise, as shown in the code below:

/** * Context object */
public class Validator {
    private final ValidationStrategy strategy;

    public Validator(ValidationStrategy v) {
        this.strategy = v;
    }

    public boolean validate(String s) {
        returnstrategy.execute(s); }}/** * Policy interface */
@FunctionalInterface
public interface ValidationStrategy {
    boolean execute(String s);
}

numericValidator = new Validator((String s) -> s.matches("[a-z]+"));
b1 = numericValidator.validate("aaaa"); // true
lowerCaseValidator = new Validator((String s) -> s.matches("\\d+"));
b2 = lowerCaseValidator.validate("bbbb"); // false
Copy the code

The Lambda strategy mode is more suitable for scenarios dealing with simple algorithm operations. If the algorithm implementation is too long and complex, it is recommended to split it into a single class for implementation.

Considerations for the policy pattern

The strategy pattern is simple to use, but its flexibility can be seen in many projects. There are also some considerations when using it. Let’s take a look at the following:

  • Each algorithm in the policy pattern is a complete, non-separable atomic business, and multiple algorithms must be interchangeable, and the external caller decides which algorithm to use.

  • If there are more than four specific policy classes, you need to use mixed mode to reduce class bloat and exposure, and fix it through other modes: factory method mode, proxy mode, and share mode

Advantages and disadvantages of strategic patterns

The introduction of a design pattern must have its reasonable place and deficiency, and finally we talk about the advantages and disadvantages of the strategy pattern.

advantages

  • With the policy mode, algorithms or behaviors can be replaced without modifying the original system, and new algorithms or behaviors can be flexibly added, which provides system scalability
  • The policy pattern provides the management and maintenance of a class of algorithms.
  • Using policy patterns eliminates the need for multiple conditional judgments, leaving external modules to decide which policy class to enforce.

disadvantages

  • The client must know all the policy classes and decide which one to use.
  • Many policy classes are created, increasing the number of items in the class.

conclusion

At this point, the study of the strategic mode is over. Of course, there is more to the content of the strategic mode, and it can be used with other modes. If you are interested, you can refer to the information link provided at the end of the article for further in-depth study. Also welcome to scan code to pay attention to wechat public number: “technology blog”, regularly share Java technology dry goods, common progress.

Recommended reading

  • Learn Spring Boot Profiles
  • How to gracefully close the Spring Boot application
  • Need interface management you know?
  • Java Lombok is a must
  • Nacos for the next generation of Java microservices

reference

  • En.wikipedia.org/wiki/Strate…
  • The design – patterns. Readthedocs. IO/zh_CN/lates…
  • Zen of Design Patterns # 18 Strategic Patterns
  • Refactoring object-oriented design patterns using Lambda in Java 8 Practice #8.2
  • Java Design Patterns #15. Strategy (Policy) Pattern

This article is published by OpenWrite! –