当前位置:网站首页>A piece of code has been refactored six times by the boss, and my mind is broken

A piece of code has been refactored six times by the boss, and my mind is broken

2022-06-11 06:18:00 Milo_

 Insert picture description here

Preface

Hi, Hello everyone , This is Milo . I'm back

Come in and give you a piece of gossip , Look what I've been doing ? Recently, the company received a new project of agricultural products trading website , Because of a code refactoring problem, I almost got involved with the boss , I thought it was the boss who deliberately made trouble for me . In the end, I found out that I was too busy , It's like this !

At the weekly meeting , The boss told us that we recently received an agricultural products trading platform , It is mainly used for online trading of agricultural products in the whole province . First of all , It's about taking our Gansu Province Yellow river honey Sell it , I was assigned to sell melons ! Ow , No , I'm responsible for developing the function of selling melons ; Soon I designed the following class to define melon Melon class :

/** *  melon  * @author Milo Lee * @date 2021-04-07 13:21 */
public class Melon {
    
    /** Varieties */
    private final String type;
    /** weight */
    private final int weight;
    /** Place of Origin */
    private final String origin;

    public Melon(String type, int weight, String origin) {
    
        this.type = type;
        this.weight = weight;
        this.origin = origin;
    }
    // getters, toString() Method ellipsis 
}

After a meal CRUD Operation , I have finished the work of adding, deleting, modifying and checking melons , Hand over work .

for the first time Select melons by type

the second day , The boss asked me a question , It can filter melons by melon type . Isn't that easy ? therefore , So I created a Filters class , Implemented a filterMelonByType Method

/** * @author Milo Lee * @date 2021-04-07 13:25 */
public class Filters {
    

    /** *  Select melons by type  * @param melons  Melon  * @param type  type  * @return */
    public static List<Melon> filterMelonByType(List<Melon> melons, String type) {
    

        List<Melon> result = new ArrayList<>();
        for (Melon melon: melons) {
    
            if (melon != null && type.equalsIgnoreCase(melon.getType())) {
    
                result.add(melon);
            }
        }
        return result;
    }
}


Get it done , So let's test that out

    public static void main(String[] args) {
    
        ArrayList<Melon> melons = new ArrayList<>();
        melons.add(new Melon(" Cornu honey ", 1, " Thailand "));
        melons.add(new Melon(" watermelon ", 2, " sanya "));
        melons.add(new Melon(" Yellow river honey ", 3, " lanzhou "));
        List<Melon> melonType = Filters.filterMelonByType(melons, " Yellow river honey ");
        melonType.forEach(melon->{
    
        System.out.println(" Melon type :"+melon.getType());
        });
    }

No problem , Show it to the boss , The boss looked at my code and said : If I ask you to add a melon by weight , How are you going to write ? Go back and think about it , This guy doesn't mean to pick on me ?

The second time Select melons by weight

Back in my seat, I thought , Last time I've implemented filtering melons by type , Then I'll give him copy Let's make a change !

As shown below :

    /**
     *  Filter melons by weight 
     * @param melons
     * @param weight
     * @return
     */
    public static List<Melon> filterMelonByWeight(List<Melon> melons, int weight) {

        List<Melon> result = new ArrayList<>();
        for (Melon melon: melons) {
            if (melon != null && melon.getWeight() == weight) {
                result.add(melon);
            }
        }
        return result;
    }

public static void main(String[] args) {
     	ArrayList<Melon> melons = new ArrayList<>();
        melons.add(new Melon(" Cornu honey ", 1, " Thailand "));
        melons.add(new Melon(" watermelon ", 2, " sanya "));
        melons.add(new Melon(" Yellow river honey ", 3, " lanzhou "));
        List<Melon> melonType = Filters.filterMelonByType(melons, " Yellow river honey ");
        melonType.forEach(melon->{
            System.out.println(" Melon type :"+melon.getType());
        });

        List<Melon> melonWeight = Filters.filterMelonByWeight( melons,3);
        melonWeight.forEach(melon->{
            System.out.println(" Melon weight :"+melon.getWeight());
        });
    }

Programmer's favorite way ,CV Get it done , ha-ha . But what I found was that filterByWeight() And filterByType() Very similar , It's just that the filtering conditions are different . I thought. , The boss won't let me write a melon selection by type and weight . Take my code and show it to the boss , Sure enough , What to be afraid of .

third time Select melons by type and weight

In order to fulfill the task of the boss , I mixed the above code together , I quickly wrote the following code

    /** *  Select melons by type and weight  * @param melons * @param type * @param weight * @return */
    public static List<Melon> filterMelonByTypeAndWeight(List<Melon> melons, String type, int weight) {
    

        List<Melon> result = new ArrayList<>();
        for (Melon melon: melons) {
    
            if (melon != null && type.equalsIgnoreCase(melon.getType()) && melon.getWeight() == weight) {
    
                result.add(melon);
            }
        }
        return result;
    }

The boss looked at my code and said , It seems that you still don't understand me . If it wasn't just me today , There are also customers who continue to demand like this .

that Filters There are going to be a lot of similar approaches , That is to say, I wrote a lot of sample code ( The code is redundant but has to be written );

In the eyes of our programmers , This is unacceptable . If you continue to add new filter conditions , The code will become difficult to maintain and error prone . You go to understand lambda expression and Functional interface Knowledge point , Modify your code again . I've made sure , He just can't get along with me

The fourth time Passing behavior as a parameter

After three ups and downs above . I found that in theory Melon Any property of a class can be used as a filter , In this case, our Filter Class will have a lot of boilerplate code , And some of them can be very complicated .

In fact, we can find that , Every time we write a method , They all correspond to a query behavior , The query behavior must correspond to a filter condition . Is there a way? Let's write a way , Pass in the query behavior as a parameter , And then return to our results ?

So give it a name : Behavior parameterization , This is illustrated in the figure below ( The left side shows what we have now ; The right side shows what we want ), Have you found that the template code will be significantly reduced

If we were to Filter conditions As an act , So it's very intuitive to think of each behavior as the implementation of the interface . After analysis, we found that all of these behaviors have one thing in common : Filter conditions and boolean Return of type . Abstract an interface as follows

public interface MelonPredicate {
    
  boolean test(Melon melon);
}

for example , Filter Yellow river honey can write like this : HHMMelonPredicate.

public class HHMMelonPredicate implements MelonPredicate {
    

     @Override
     public boolean test(Melon melon) {
    
       return " Yellow river honey ".equalsIgnoreCase(melon.getType());

     }

}

And so on , We can also filter a certain weight of melon :

public class WeightMelonPredicate implements MelonPredicate {
    

     @Override
     public boolean test(Melon melon) {
    
       return melon.getWeight() > 5000;
     }

}


In fact, students who are familiar with design patterns should know that this is : Strategy design pattern .

The main idea is to let the system dynamically select the method to be called at runtime . So we can say MelonPredicate The interface unifies all the algorithms used to filter melons , And each implementation is a strategy , We can also think of it as an act .

at present , We use strategic design patterns , Abstract the query behavior . We also need a way to receive MelonPredicate Parameters . So I defined filterMelons() Method , As shown below :

public static List<Melon> filterMelons(List<Melon> melons, MelonPredicate predicate) {
    
    
    List<Melon> result = new ArrayList<>();
    for (Melon melon: melons) {
    
      if (melon != null && predicate.test(melon)) {
    
        result.add(melon);
      }

    }  
    return result;
}


Be accomplished , Test it , Sure enough, it's much easier to use than before , Let the boss see it again

List<Melon> hhm = Filters.filterMelons(melons, new HHMMelonPredicate());

List<Melon> weight = Filters.filterMelons(melons, new WeightMelonPredicate());


The fifth time One time plus 100 Filter conditions

Just when I was smug , The boss poured cold water on him again . He said that you think our platform will buy yellow river honey , If there are dozens of melon varieties before and after , I'll give you a list of 100 Two filter conditions , What would you do ?

In my heart, ten thousand grass mud horses are galloping ! Does the boss mean to have a hard time with me ! Although after the last transformation , My code is flexible enough , But if it suddenly increases 100 Filter conditions , I still need to write 100 A policy class to implement Every filter condition . Then we need to pass the strategy on to filterMelons() Method .

Is there a way not to create these classes ? Smart I soon found that I could use java Anonymous inner class .

As shown below :

List<Melon> europeans = Filters.filterMelons(melons, new MelonPredicate() {
    

     @Override

     public boolean test(Melon melon) {
    

       return "europe".equalsIgnoreCase(melon.getOrigin());

     }

});


Although a big step forward , But it doesn't seem to help . I still need to write a lot of code to achieve this requirement . The purpose of designing anonymous inner classes , Just for the convenience of Java Programmers pass code as data . occasionally , Anonymous inner classes are more complicated , Now , It suddenly occurred to me that the boss asked me to learn lambda expression , I can use it to simplify

List<Melon> europeansLambda = Filters.filterMelons(
  melons, m -> "europe".equalsIgnoreCase(m.getOrigin())
);

Sure enough, it's much more handsome !!!, That's it , Once again, I successfully completed the task . I excitedly took the code to the boss to have a look .

The sixth time Introduce generics

The boss looked at my code and said , Um. , Pretty good ! The brain is finally opening up . Now think about it , Our platform is for agricultural products , That is, there must be more than melons , If you make other fruits , How to modify your code ?

At present, our MelonPredicate Support only Melon class . What's wrong with this guy ? Maybe one day he'll buy vegetables 、 What's wrong with sea cucumbers , We can't create a lot of similar things for him MelonPredicate It's a good interface . At this time, I suddenly remembered what the teacher said Generic , It's time for it to work !

So I defined a new interface Predicate

@FunctionalInterface
public interface Predicate<T> {
    

  boolean test(T t);

}


Next , We rewrite the filterMelons() Method and rename it to filter()

public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
    
  
	List<T> result = new ArrayList<>();

    for (T t: list) {
    

      if (t != null && predicate.test(t)) {
    

        result.add(t);

      }

    }  

    return result;

}


Now? , We can filter melons like this :

List<Melon> watermelons = Filters.filter(

  melons, (Melon m) -> "Watermelon".equalsIgnoreCase(m.getType()));


alike , We can do the same thing with numbers :

List<Integer> numbers = Arrays.asList(1, 13, 15, 2, 67);

List<Integer> smallThan10 = Filters.filter(numbers, (Integer i) -> i < 10);


Let's go back and make a repeat , We found that since using Java 8 Functional interfaces and lambda After expression , The code has changed dramatically .

I don't know if the careful partner has found our above Predicate There's one more on the interface @FunctionalInterface The annotations on , It's the mark Functional interface Of .

thus , We go through an evolution of requirements , I understand lambda And functional interfaces , At the same time, we should deepen our understanding of them . In fact, I am familiar with java8 Our friends all know , In our java.util.function The package contains 40 Multiple such interfaces

Functional interface and lambda expression Formed a strong team . According to the example above , We know that functional interfaces are a high abstraction of our behavior ,lambda Expression we can see an example of a concrete implementation of this behavior .

Predicate<Melon> predicate = (Melon m)-> "Watermelon".equalsIgnoreCase(m.getType());

In short Lambda

lambda Expression consists of three parts , As shown in the figure below :

Here are lambda The description of each part of the expression :

  • On the left side of the arrow , Is in lambda Parameters used in the body of the expression .
  • On the right side of the arrow , yes lambda The main body .
  • The arrow is just lambda The separator between the parameter and the body .

this lambda The anonymous class version of is as follows :

List<Melon> europeans = Filters.filterMelons(melons, new Predicate<Melon>() {
    

 @Override

 public boolean test(Melon melon) {
    

   return "Watermelon".equalsIgnoreCase(melon.getType());

 }

});

Now? , If we look at lambda expression And its An anonymous class edition , It can be described from the following four aspects lambda expression

We can lambda An expression is defined as a concise 、 Transitive anonymous functions , First of all, we need to be clear lambda An expression is essentially a function , Although it doesn't belong to a particular class , But with a parameter list 、 Function main body 、 Return type , It can even throw an exception ; Second, it's anonymous ,lambda The expression has no specific function name ;lambda Expressions can be passed like arguments , So as to simplify the code writing .

Lambda Support Behavior parameterization , In the previous example , We have proved that . Last , please remember ,lambda Expressions can only be used in the context of functional interfaces .

summary

In this paper , We focus on Functional interface The purpose and availability of , We'll look at how to evolve code from the original template code to a flexible implementation based on functional interfaces . Hope to help you understand the functional interface , Thank you. .

原网站

版权声明
本文为[Milo_]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/03/202203020528131133.html