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

  1. IF-ELSECode blocks, transformation time is short, not suitablebone
  2. Use strategy mode to eliminateIF-ELSEThe code block is complex and large, adding many policy classes
  3. 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

  1. 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

  1. 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{

    apply(T t);

}

@FunctionalInterface

public interface BiFunction<T.U.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

  1. 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

  1. 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
  1. 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
  1. 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