Usage scenarios
The strategy pattern is a common behavior design pattern. To put it simply, in the system, for many types of judgment, the strategy pattern can help us avoid if… Else judgment, and the ability to dynamically select different implementations for different types, providing high scalability.
For example: there are many kinds of beauty, such as royal sister type, girl type, temperament type, so for you, for each type of sister, the method of confession must be different. Now like is royal elder sister type, for example, you can write an interface that defines a method to the royal elder sister, so you don’t just like royal elder sister in the future, also like a girl, how do you go to expand, at this time that you need to tell others, now you don’t just like royal elder sister also like a girl, need to add the if the else judge each other in the code is royal elder sister or a girl, To choose the corresponding method of confession, at this time the use of strategy mode can achieve automatic judgment of the other party is royal sister type or girl type, and automatically choose the corresponding method of confession
A policy pattern is one in which an object has a behavior, but in different scenarios, there are different algorithms for implementing that behavior, each of which is encapsulated in a separate class with a common interface, making them interchangeable. The policy pattern allows the algorithm to change without affecting the client.
The practical application
Let’s just look at a practical example. Suppose you have a requirement to listen for events in GitLab, as you can see in the following figure.
Similarly, if we are dealing with multiple policies in an interface, the policy pattern can dynamically allow an object to choose one behavior among many
- 1. Define a policy interface
public interface GitEventStrategy {
/** *@paramJsonObject listens for packets *@return* /
void handleEvent(JSONObject jsonObject) throws Exception;
}
Copy the code
- 2. Define a policy environment class and select different implementations according to different event types in packets. BeanName is mapped to the type of the event, and the choice of policy implementation depends
jsonObject.getString("object_kind")
The value of the.
@Component
public class GitEventStrategyContext {
@Autowired
private Map<String, GitEventStrategy> strategyMap;
public void handle(JSONObject jsonObject) throws Exception {
if(strategyMap ! =null) {
GitEventStrategy eventStrategy = strategyMap.get(jsonObject.getString("object_kind"));
if (eventStrategy == null) {
return; } eventStrategy.handleEvent(jsonObject); }}}Copy the code
- 3. Define specific policy implementations
@Service("merge_request")
public class GitMergeImpl implements GitEventStrategy {
@Override
public void handleEvent(JSONObject jsonObject) throws Exception {... }}Copy the code
- 4. Client call
@RequestMapping("/gitEvent")
public class GitEventController {
@Autowired
GitEventStrategyContext gitEventStrategyContext;
@apiOperation (httpMethod = "POST", value = "event listener ")
@RequestMapping("handle")
public MessageBean handle(@RequestBody JSONObject jsonObject, HttpServletRequest request) throws Exception {
gitEventStrategyContext.handle(jsonObject);
returnMessageBean.success(); }}Copy the code
Only the Merge_request event is implemented here. If you want to extend the implementation of other events, you just need to add an implementation class for the specific event. Is it convenient?
As you can see in the above example, different events can be switched dynamically while avoiding the use of if… Else determines the different events, and it’s very easy to extend the different events, which is the benefit of the policy pattern
structure
In policy mode, there are three roles
- Abstract Strategy Roles: Typically implemented by an interface or abstract class. This role provides all the interfaces required by the specific policy class, as described above
GitEventStrategy
interface - Context role: Holds a reference to Strategy, as above
GitEventStrategyContext
- ConcreteStrategy role: Encapsulates the associated algorithm or behavior, as described above
GitMergeImpl
The use of policy patterns in ThreadPoolExecutor
Do you know what happens when the queue in the thread pool is full? The policy pattern is also used extensively in JDK source code. This chapter describes how to implement the rejection policy of ThreadPoolExecutor using the policy pattern
Abstract Policy class
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
Copy the code
Different RejectedexecutionHandlers can be selected to implement the RejectedExecutionHandler through the Constructor of ThreadPoolExecutor
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
When a task is added to the thread pool using the execute(Runnable) method, the core thread corePoolSize, the workQueue, and the maximum thread maximumPoolSize are all full, and the corresponding handler is used to process the rejected task
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null.false);
}
else if(! addWorker(command,false))
reject(command);
}
Copy the code
Executing a rejection Policy
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
Copy the code
The four different implementations are as follows:
- Call ThreadPoolExecutor. The execute method of thread running in the rejected tasks, unless the executable program has been closed, in this case the task is discarded
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy(a) {}public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if(! e.isShutdown()) { r.run(); }}}Copy the code
- Direct selling Java. Util. Concurrent. RejectedExecutionException anomalies
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy(a) {}public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from "+ e.toString()); }}Copy the code
- Discard policies. Tasks that cannot be executed are simply removed and, unlike strategy 1, no exceptions are thrown
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy(a) {}public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}}Copy the code
- Tasks in the task queue header are discarded and retry
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy(a) {}public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if(! e.isShutdown()) { e.getQueue().poll(); e.execute(r); }}}Copy the code
So what’s the advantage of this? The RejectedExecutionHandler interface can be implemented and the RejectedExecutionHandler method can be used to extend the RejectedExecutionHandler.