God loves apples

At present, shell House is my employer in The Magic city, usually pay attention to some Java field related technology, I hope you can find something useful in this article. Personal level is limited, if there are mistakes in the article, please also point out, in the message area with communication.

I’m sure you’ve all seen a lot of “strategy models”, sermons, etc. This article is intended to “clarify” the strategy model and try to answer the following questions:

  1. How do policy patterns optimize the business logic code structure?
  2. How can you kill a chicken with a knife? Just how many if else scenarios do I need to use the policy mode? !
  3. Is there a better code structure to implement the policy pattern?

How do policy patterns optimize the business logic code structure?

To answer this question, we need to take a look at the definition of the strategic pattern and understand it from the definition

Textbook definitions of policy patterns

Its definition is minimal: the behavior of a class or its algorithm can be changed at run time. Let’s scale it down to the code level, which translates in human terms to say, at runtime I give you a different “key” for a method of this class, and your method will execute a different business logic. That’s what the if else does.

What does the strategy pattern optimize?

In fact, the core idea of the strategy pattern is the same as if else, according to the different key dynamic find different business logic, so it is only that?

In fact, what we call the policy pattern is actually tweaking the code structure with interfaces + implementation classes + dispatch logic to make the code structure more maintainable.

While most textbooks cover interfaces and implementation classes, other blogs will cover dispatching logic. I won’t bore you here.

To summarize, even with the policy mode, the business logic you should write is written as usual, and when the logic is dispatched, it is an if else variant. Its optimization point is the abstraction of the interface, the business logic into a package of implementation classes, arbitrary replacement. This is easier to maintain in complex scenarios (with more business logic) than if else.

How can you kill a chicken with a knife? Just how many if else scenarios do I need to use the policy mode? !

I think people often complain that my business logic is 3 or 4 lines, and you give me a whole bunch of class definitions? Is it necessary to go to all this trouble? I think the specific business logic also needs to go to different classes, simple line.

It is the disadvantages of the strategic model that we are unhappy with:

2. Business logic is scattered across implementation classes, with no one place to look down on the entire business logic

In view of the disadvantages of the traditional strategy mode, I share an implementation idea here. This idea has helped our team solve many complex if else business scenarios, which is easy to understand. The code needs to use the features of Java8 — Map and functional interface to achieve.

Direct show code structure: To demonstrate a simple idea, the code uses String to simulate a business BO

GetCheckResultSuper () is a traditional method. GetCheckResultSuper () defines the mapping between the “judgment criteria” and “service logic” in the Map. For details, see code comments

/** * A business Service class */ @service public class BizService {/** * the traditional if else solution * when each business logic has 3 or 4 lines, it is not worth using the traditional policy pattern, */ public String getCheckResult(String order) {if (" check 1". Equals (order)) {return "execute business logic 1"; } else if (" check 2". Equals (order)) {return "execute business logic 2"; }else if (" check 3". Equals (order)) {return "execute business logic 3"; }else if (" check 4". Equals (order)) {return "execute business logic 4"; }else if (" check 5". Equals (order)) {return "execute business logic 5"; }else if (" check 6". Equals (order)) {return "check business logic 6"; }else if (" check 7". Equals (order)) {return "check business logic 7"; }else if (" check 8". Equals (order)) {return "execute business logic 8"; }else if (" check 9". Equals (order)) {return "execute business logic 9"; } return "business error not returned in processing logic "; } /** * the business logic dispatches Map * Function as a functional interface. Function<String, String> */ Private Map<String, Function<String, String>> checkResultDispatcher = new HashMap<>(); / * * * initializes the business logic dispatch Map the deposit the value of which is lambda expressions * / @ PostConstruct public void checkResultDispatcherInit () { CheckResultDispatcher. Put (" check 1 ", the order - > String. The format (to perform business logic 1 "% s", the order)); CheckResultDispatcher. Put (" check 2 ", the order - > String. The format (" execute the business logic for % s 2 ", the order)); CheckResultDispatcher. Put (" three check ", the order - > String. The format (" execute the business logic for % s 3 ", the order)); CheckResultDispatcher. Put (" check 4 ", the order - > String. The format (" execute the business logic for % s 4 ", the order)); CheckResultDispatcher. Put (" check 5 ", the order - > String. The format (" execute the business logic for % s. 5 ", the order)); CheckResultDispatcher. Put (" 6 "check, order - > String. The format (" execute the business logic for % s 6", the order)); Check (checkResultDispatcher. Put (" 7 ", the order - > String. The format (to perform business logic 7 "% s", the order)); CheckResultDispatcher. Put (" check 8 ", the order - > String. The format (" execute the business logic for % s 8 ", the order)); CheckResultDispatcher. Put (9 "check", the order - > String. The format (" execute the business logic for % s 9 ", the order)); } public String getCheckResultSuper(String order) { Result variable is a lambda expression Function < String, the String > result = checkResultDispatcher. Get (order); if (result ! Return result.apply(order); return result.apply(order); } return "business error not returned in processing logic "; }}Copy the code

Use HTTP to see what happens:

@restController Public Class BizController {@autoWired private BizService BizService; @PostMapping("/v1/biz/testSuper") public String test2(String order) { return bizService.getCheckResultSuper(order); }}Copy the code

This is a simple demo, followed by some complex scenarios.

Lu Xun once said, “For every problem solved, more problems will arise.” Let’s take a look at the benefits and problems of such an implementation.

The benefits are straightforward:

  1. Visually see the mapping between “judgment criteria” and business logic in a piece of code
  2. Instead of defining separate interfaces and implementation classes, use existing functional interfaces (what? Don’t know functional interfaces? The implementation class is directly the business code itself.

Cons: 1. Requires team members to have some knowledge of lambda expressions (what? Java14 has come out, who hasn’t used the new features of Java8?

Next, I’ll take a few if else scenarios that are commonly encountered in business and use Map+ functional interfaces to solve them

Solutions to problems in real business scenarios

Some people might say, “Well, I have multiple judgments, and it’s complicated. You gave me an example where YOU only have one judgment logic, and I have multiple judgment logics. What do I do?”

Easy to solve: write a method to determine the logic, the Map key calculated by the method

/** * public class BizService {private Map<String, Function<String, String>> checkResultDispatcherMuti = new HashMap<>(); / * * * initializes the business logic dispatch Map the deposit the value of which is lambda expressions * / @ PostConstruct public void checkResultDispatcherMuitInit () { CheckResultDispatcherMuti. Put (" 1 "key_ order, the order - > String. The format (to perform business logic 1" % s ", the order)); CheckResultDispatcherMuti. Put (" key_ order 1 _ order 2 ", the order - > String. The format (" execute the business logic for % s 2 ", the order)); CheckResultDispatcherMuti. Put (" key_ order 1 _ 2 _ order 3 "order, order - > String. The format (" execute the business logic for % s 3", the order)); } public String getCheckResultMuti(String order, int level) {  String ley = getDispatcherKey(order, level); Function<String, String> result = checkResultDispatcherMuti.get(ley); if (result ! Return result.apply(order); return result.apply(order); } return "business error not returned in processing logic "; } private String getDispatcherKey(String order, int level) { StringBuilder key = new StringBuilder("key"); for (int i = 1; i <= level; i++) { key.append("_" + order + i); } return key.toString(); }}Copy the code

Use HTTP to simulate:

@restController Public Class BizController {@autoWired private BizService BizService; @PostMapping("/v1/biz/testMuti") public String test1(String order, Integer level) { return bizService.getCheckResultMuti(order, level); }}Copy the code

Just design your key generation rules.

And friends will ask: I have a lot of many lines of business logic, in checkResultDispatcherMuitInit () method to write directly in the Map will not very long?

We can abstract a service that holds the business logic, and then call it in the definition:

Provide a business logic unit:

@service public class BizUnitService {public String BizUnitService (String order) {return order + "BizUnitService "; } public String bizTwo(String order) {return order + "bizTwo "; } public String bizThree(String order) {return order + "bizThree "; }}Copy the code
@service public class BizService {@autoWired private BizUnitService BizUnitService; private Map<String, Function<String, String>> checkResultDispatcherComX = new HashMap<>(); / * * * initializes the business logic dispatch Map the deposit the value of which is lambda expressions * / @ PostConstruct public void checkResultDispatcherComXInit () { CheckResultDispatcherComX. Put (" 1 "key_ order, the order - > bizUnitService. BizOne (order)); CheckResultDispatcherComX. Put (" key_ order 1 _ order 2 ", the order - > bizUnitService. BizTwo (order)); CheckResultDispatcherComX. Put (" key_ order 1 _ 2 _ order order 3 ", the order - > bizUnitService. BizThree (order)); } public String getCheckResultComX(String order, int level) {public String getCheckResultComX(String order, int level) {  String ley = getDispatcherComXKey(order, level); Function<String, String> result = checkResultDispatcherComX.get(ley); if (result ! Return result.apply(order); return result.apply(order); } return "business error not returned in processing logic "; } private String getDispatcherComXKey(String order, int level) { StringBuilder key = new StringBuilder("key"); for (int i = 1; i <= level; i++) { key.append("_" + order + i); } return key.toString(); }}Copy the code

Call result:

conclusion

Finally, let’s try to answer the following questions: 1. How does the policy pattern optimize the business logic code structure?

Abstract out of the interface, the business logic into a package of implementation classes, arbitrarily replaced. This is easier to maintain in complex scenarios (with more business logic) than if else.

  1. How can you kill a chicken with a knife? Just how many if else scenarios do I need to use the policy mode? !

What we complain about is the shortcomings of traditional interface implementations: 1. There are many policy classes. 2. Business logic is scattered across implementation classes, and there is no one place to view the entire business logic

  1. Is there a better code structure to implement the policy pattern?

Aiming at the disadvantages of traditional policy mode, the idea of using Map and functional interface is shared.