Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

Everyone more or less likely for a member of the benefits of membership is relatively non-members will be able to get a bigger discount, routines are more, of course, like you want to learn a skill, spent a lot of money for a member of learning a skill, and then the recent training institutions new introduced a better quality of a course, but in the face of different members buy discount of this course is not the same, If you are a regular member, you will only get 20% off, if you are a VIP member, you will get 60% off, and if you are not a member, you will not get any discount, so now we use code to implement this scenario.

public class Store { public BigDecimal quote(BigDecimal bigDecimal, String type) {if (" newMember ". Equals (type)) {return newMember(bigDecimal); } else if (" oldMember ". Equals (type)) {return oldMember(bigDecimal); } else if (" vipMember ". Equals (type)) {return vipMember(bigDecimal); } return null; } // New members do not discount public BigDecimal newMember(BigDecimal BigDecimal) {return BigDecimal; } // 20% off public BigDecimal oldMember(BigDecimal BigDecimal) {return BigDecimal. Multiply (new BigDecimal(0.8)); } public BigDecimal vipMember(BigDecimal BigDecimal) {return BigDecimal. Multiply (new BigDecimal(0.6)); }}Copy the code

By creating a discount method, then use if statements to determine different users to much of a discount, but if we added a member, such as the super VIP member, so we should not only change the original methods, adding an if judgment, but also how to write a specific discount method, it’s not conform to the open closed principle, cannot be flexible to response of demand change, So this is where the strategy model comes in.

The idea of the policy pattern is to take a set of algorithms and encapsulate each algorithm into a separate class with a common interface so that they can be replaced with each other. The biggest characteristic of policy mode is that the algorithm can change without affecting the client, so as to change different functions. It is composed of abstract policy roles, policy concrete roles, and policy context. The class diagram and code are described below.

Public abstract class Strategy {public abstract void AlgorithmInterface(); } # ConcreteStrategyA extends Strategy public void AlgorithmInterface() { System.out.println(" algorithm A implementation method "); }} # concreteStrategb extends Strategy{@override public void AlgorithmInterface() { System.out.println(" algorithm B implementation method "); }} # Strategy Context public class Context {private Strategy Strategy; public Context(Strategy strategy){ this.strategy=strategy; } / / according to the specific policy object to call its algorithm public void contextInterface () {strategy. AlgorithmInterface (); }}Copy the code

Through class diagram and code, the relationship and code realization of abstract class, context and concrete policy class are shown.

  • Policy Abstract Class: This is an abstract role, usually implemented using interfaces or abstract classes.

  • Concrete policy classes: wrap concrete algorithms and behaviors.

  • Policy context: A reference to an abstract role is held internally for the client to invoke.

Through the strategy mode, we transform the example of membership discount at the beginning. Different types of customers have different discounts. We can encapsulate the quotation rules of different types of customers into an independent algorithm, and then abstract out the common interface of these quotation algorithms.

Public abstract class StoreStrategy {public abstract BigDecimal quote(BigDecimal BigDecimal); } # public class NewMember extends StoreStrategy {@override public BigDecimal quote(BigDecimal BigDecimal) { return bigDecimal; Public class OldMember extends StoreStrategy {@override public BigDecimal quote(BigDecimal BigDecimal) {return bigDecimal. Multiply (new bigDecimal (0.8)); }} public class VipMember extends StoreStrategy {@override public BigDecimal quote(BigDecimal) BigDecimal) {return bigDecimal. Multiply (new bigDecimal (0.6)); Public class StoreContext {StoreStrategy StoreStrategy; public StoreContext(StoreStrategy storeStrategy) { this.storeStrategy = storeStrategy; } public BigDecimal quote(BigDecimal bigDecimal) { return storeStrategy.quote(bigDecimal); }} # ConcreteStrategyA public Class StrategyTest {public static void main(String[] args) {ConcreteStrategyA =new ConcreteStrategyA(); Context context=new Context(concreteStrategyA); context.contextInterface(); }}Copy the code

After the transformation of the policy mode, it can be seen that even if new members are added or there is a new discount way, it only needs to create a specific policy class to inherit the abstract policy class. The specific choice of which strategy only needs to be made by the client, in line with the open and closed principle.

The policy pattern is also widely used in the JDK. For example, in multithreaded programming, thread pools are often used to manage threads to reduce the waste of resources caused by frequent thread creation and destruction. Normally, we use a factory class to create the thread pool Executors. Actually, we use the ThreadPoolExecutor class inside the Executors. It has a final constructor as follows:

  public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }
Copy the code
  • CorePoolSize: The number of core threads in the thread pool that are not destroyed even if they have no work.

  • MaximumPoolSize: Maximum number of threads that can be created in the thread pool.

  • KeepAliveTime: The maximum amount of time that extra threads wait for a new task when the number of threads in the thread pool is greater than corePoolSize.

  • Unit: keepAliveTime Time unit.

  • WorkQueue: A queue of tasks that are stored in the workQueue before a thread in the thread pool has completed a task. (While all threads in the thread pool have completed a task, the task is still submitted and stored in the workQueue.)

  • RejectedExecutionHandler is a policy interface for executing tasks that are still being submitted to the thread pool if there are no more threads in the current thread pool and the columns holding the task are full (i.e. bounded queues).

public interface RejectedExecutionHandler {

    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

Copy the code

The policy has four implementation classes:

  • AbortPolicy: the task of this strategy is submitted directly to the abandoned, and throw RejectedExecutionException anomalies.
   public static class AbortPolicy implements RejectedExecutionHandler {

        public AbortPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }

Copy the code
  • DiscardPolicy: This policy also discards the task (don’t ask, don’t do anything about the submitted task), but doesn’t throw an exception.

    public static class DiscardPolicy implements RejectedExecutionHandler {

        public DiscardPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }
Copy the code
  • DiscardOldestPolicy: Removes the first task from the workQueue when the actuator is not closed and discards the first task so that there is space to store the newly committed task. Use this strategy with extreme caution, as it directly abandons the previous task.
public static class DiscardOldestPolicy implements RejectedExecutionHandler { public DiscardOldestPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (! e.isShutdown()) { e.getQueue().poll(); e.execute(r); }}}Copy the code
  • CallerRunsPolicy: This policy does not discard any task, since there are no extra threads in the thread pool to assign the task, this policy executes the task directly in the current thread (the caller thread).
public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (! e.isShutdown()) { r.run(); }}}Copy the code
  • EadPoolExecutor holds a reference to the RejectedExecutionHandler interface so that specific policies can be specified and injected by the client in the constructor.

The strategy pattern is not really about how we implement algorithms, but how we call and organize them to make our code more flexible and extensible.

The strategy pattern is a set of algorithms that are interchangeable, so when you write them together it’s essentially an if and else structure, and if there are conditional statements in the algorithm, you can use the strategy pattern to avoid such conditional statements.

If you think the article is good, please follow me