Change going
Recently, due to the reconstruction of a project, I had time to transform the old code. I found that many interfaces were well designed at the initial stage, but under the attack of various demands, they became more and more chaotic, and finally became the so-called “garbage heap”, with all kinds of IF-else flying everywhere. Those who took over complained. Finally can only smell the stench, holding the nose to continue to “garbage” in the garbage. Eventually the dump collapsed and a new project began
trouble
IF-ELSE
Code blocks, transformation time is short, not suitable“bone“- Use strategy mode to eliminate
IF-ELSE
The code block is complex and large, adding many policy classes - Try to reuse existing logic, do not add new code
income
Java8 has been used for some time, like “Steam” data processing, feel not writing Java code at all, complex code no more, streamlined code to write, uh… The Java8 package “Function” has been added to the system for a long time, but there are a lot of tools in this package to simplify our work
Note: the following code only gives the core part, other methods please check the source code
- Predicate BiPredicate
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
@FunctionalInterface
public interface BiPredicate<T.U> {
boolean test(T t, U u);
}
Copy the code
❝
The first tool is called Predicate. What does it do? As the name implies, it provides a judgment logic that can replace the “hook” method in “policy mode”, which determines which policy to use to process the current logic. Of course, “hooks” can be provided by the factory method, but the minimalist strategy option places “hooks” inside the policy: I’ll give you two predicates just to make it easier for you to deal with multiple inputs
❞
- Consumer Function
//Consumer
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
@FunctionalInterface
public interface BiConsumer<T.U> {
void accept(T t, U u);
}
//Function
@FunctionalInterface
public interface Function<T.R> {
R apply(T t);
}
@FunctionalInterface
public interface BiFunction<T.U.R> {
R apply(T t, U u);
}
Copy the code
❝
The second tool is Consumer or Function. These two tools represent two situations that the strategy deals with, namely, the first is pure consumption (inventory, etc.), and the second is: There is production (filtering data, etc.), which is used to replace the policy part of the policy pattern, eliminating the need to write complex policy interfaces and policy implementation classes, simplifying the scale of the policy pattern, and reducing the code and time costs
❞
- Pair
/ / the author use org. Springframework. Data. Util next Pair of tools
// Because no additional dependencies need to be introduced
// PS: Pair implementation is many, can choose to eat
public final class Pair<S.T> {
@NonNull
private final S first;
@NonNull
private final T second;
public static <S, T> Pair<S, T> of(S first, T second) {
return new Pair(first, second);
}
public S getFirst(a) {
return this.first;
}
public T getSecond(a) {
return this.second;
}
}
Copy the code
❝
Obviously, “Pair” is used to store policies, so that “hook” and “policy logic” are assembled into “policy processing unit”, which is basically a tool container. There are many kinds of containers, so you can find one you like, I just choose the nearest one
❞
Have a try
❝
Often someone in the group to ask some actually write a Main method can test out the question, and then everyone’s question and answer is: you try! So, programmers must be able to try, so try first
❞
- The first step is to find an if-else block
A simple permission filtering function, can write 3 IF nested, logic complex?
// The PowerQuery structure is below
public List<String> filterPowerByQuery(List<String> powers, PowerQuery query) {
if ("ALL".equalsIgnoreCase(query.getType())) {
// Internal and external users ALL Returns ALL
return powers;
}
if (new Integer(0).equals(query.getUserType())) {
// Internal user
if (StringUtils.isNotBlank(query.getPowerName())) {
// Internal users can view type permissions prefixed with PowerName.
return powers.stream()
.filter(s -> StringUtils.startsWithIgnoreCase(s, query.getPowerName()))
.collect(Collectors.toList());
}
// Other information about internal users
return powers;
} else {
// In the non-all case, external users can view data with only one permission at a time
return powers.stream()
.filter(s -> StringUtils.equals(query.getPowerName(), s))
.collect(Collectors.toList());
}
}
Copy the code
The structure of PowerQuery consists of three simple attributes
@Data
public class PowerQuery {
/ * *
* ALL - ALL of them
* Other values - invalid
* /
private String type;
/ * *
* 0 - inside
* 1 - outside
* /
private Integer userType;
/ * *
* If not ALL view
* External users can view only one permission at a time
* /
private String powerName;
}
Copy the code
- Offer him
public List<String> filterPowerByStrategy(List<String> allPowers, PowerQuery powerQuery) {
// The policy pattern in this example has obvious chained rules
// But using a List can also reflect this rule nicely
// The various resolvers in DispatchServlet like Spring are also organized by List
List<Pair<Predicate<PowerQuery>, BiFunction<List<String>, PowerQuery, List<String>>>> chains = new ArrayList<>();
/ / ALL logic
chains.add(Pair.of(query -> "ALL".equalsIgnoreCase(query.getType()), (powers, query) -> powers));
// The external user logic is brought up here
chains.add(Pair.of(query -> new Integer(1).equals(query.getUserType()), (powers, query) -> powers));
// Internal user and PowerName has a value
chains.add(Pair.of(query -> new Integer(0).equals(query.getUserType()) && StringUtils.isNotBlank(query.getPowerName()),
(powers, query) -> powers.stream()
.filter(s -> StringUtils.startsWithIgnoreCase(s, query.getPowerName()))
.collect(Collectors.toList())));
// Add a last policy and return the original full permission in other cases
chains.add(Pair.of(query -> true, (powers, query) -> powers));
// Use policy List
for (Pair<Predicate<PowerQuery>, BiFunction<List<String>, PowerQuery, List<String>>> chain : chains) {
if (chain.getFirst().test(powerQuery)) {
return chain.getSecond().apply(allPowers, powerQuery);
}
}
// This logic will not work
return allPowers;
}
Copy the code
- Way to describe
First, the existing logic is sorted out, the processing logic of the policy is stripped, and then the policies are grouped together using Predicate and Function to form a cluster of methods in the policy mode. Finally, the policy hook is hit by “loopout”, and the policy logic is “run processing”. There are a lot of imitation traces in the proxy. For example, policies use “List” organization, “loop out” for logical processing
Always a knot
❝
The author is very fond of strategy mode, and will try to use strategy mode in daily development. Also realized in the process of practice, the strategy pattern using the threshold to a certain extent, and feel the strategy pattern dimension, each attempt using the implementation, strategy of “interface” is a top, add a lot of strategy implementation method of the “clusters”, but actually there is most in need of policy change IF – ELSE, but a make you very trouble, The back took over the brother is also a face meng force, big shout please let me see for a while… This minimalist strategy is “more about simplicity”, not adding too many classes and interfaces, simply converting if-else, with no significant increase in code volume, and also supports “rapid expansion”, as mentioned in the article, which is really the result of the change. At the same time, I also want to point out that this is also the result of the author “behind closed doors”, which is inevitable omissions. Today, I share it with you in the hope of giving you some inspiration, casting a brick to attract gems and seeking common ground while reserving differences. If you have any ideas, please leave a comment below for discussion
❞