Behavioral parameterization is a software development model that can help you handle frequent change requirements. It means to take out a block of code, get it ready but not execute it. This block of code can later be called by other parts of your program, which means you can postpone the execution of this code. For example: You can pass a block of code as an argument to another method and then execute it later.
Responding to changing needs
1. First try: Implement a feature that filters out the green Apple functionality from a list.
Prepare the Apple entity class first
Public class Apple { private Integer Id; Private String Color; Private Double Weight; //getter. Setter.. ToString.. }
Write the function of filtering out green apples
Public Static List<apple> filter (list<apple> apples) { Listnew arraylist<>(); for (Apple apple:apples) { // filter out green apples if("green" ) . Equals (Apple.getcolor ())) { result.add (apple); } } return result; }
Test data
public static void main (string[] args) {List <Apple> apples = Arrays.aslist ( new Apple (1, "green", 18.0 new Apple (2, "Yellow" ,36d", new Apple (3, "Red" , 42d), new Apple (4, "green" ,15d), new Apple (5, "Red" ,16d)); List <Apple> greenapple = filter (apples); System.out.println (greenapple); }
Output Result:
[Apple{id=1, color= ' green ', weight= ' 18.0 '}, apple{id=4, color= ' green ', weight= ' 15.0 '}]
To realize the function, now the product said I want to filter the Red Apple, the simplest way is to copy this method, the name is changed to Filterredapples, and then change the if condition to match the red Apple, but the product wants to filter more colors of apples, yellow, orange, big mole, etc. This method is not going to be the color as a parameter
2. Second attempt: color as a parameter
// use color as a parameter Public Static List<apple> filterapplesbycolor (list<apple> apples,String color) { ListNew arraylist<>(); for (Apple apple:apples) { if(Color.equals (Apple.getcolor ())) { Result.add (apple); } } return result; }
Now just call this, and the product will be satisfied.
list<apple> yellowapple = Filterapplesbycolor (apples, "yellow"); List<Apple> redapple = Filterapplesbycolor (apples, "red");
Then the product ran over to say, if can distinguish Apple size is too good, more than 32 divided into big apples, less than 32 is divided into small apples.
So you took the following method, and added the weight of the parameter
// To filter apples by weight Public Static List<apple> filterapplesbyweight (list<apple> apples,Double weight) { ListNew arraylist<>(); for (Apple apple:apples) { if(apple.getweight () > weight) { Result.add (apple); } } return result; }
You have finally achieved the demand for the product, but note that you copied most of the code to implement the traversal of the Apple list and filter the criteria for each apple. This is a bit disappointing because it breaks the software engineering principles of don ' t Repeat yourself (don't repeat yourself).
3. Third attempt: Filter every attribute you can think of
You can combine color and weight into one method, called Filtercolororweight, and then add a parameter to distinguish which property needs to be filtered.
//Filter by color or by weight Public StaticList<apple> filtercolororweight (list<apple> apples, String color, Double weight,Boolean flag) {List<Apple> result =NewArraylist<>(); for(Apple apple:apples) { if (flag && color.equals (apple.getcolor)) | | (!flag && apple.getweight () > weight)) { result.add (Apple); } } returnresult; }
Then you can use this:
list<apple> yellowapples = filtercolororweight (apples, "yellow", 0d,true); List<Apple> bigapples = filtercolororweight (Apples, "", 32d,false);
This solution is no worse, first the client code looks rotten, what does true and false mean? In addition, the solution is not a good area to cope with the changing needs, if the product lets you to the other different properties of Apple screening, such as size, shape, origin, etc., what should be done? Or ask you to combine attributes to make more complex queries, such as Big green apples, what should I do?
Parameterization of Behavior
As you can see in the previous section, you need a better way to cope with the changing needs than to add a lot of parameters. One solution is to model the criteria you choose: For example, to return a Boolean value based on some of Apple's properties (whether green or not), we call it a predicate . Let's start by defining an excuse to model the selection criteria.
Public Interface applepredicate { boolean Test (Apple apple);}
You can now use multiple implementations of applepredicate to represent different criteria for selection, such as:
public class Applegreencolorpredicate implements applepredicate {//green apple verb public Span style= "COLOR: #0000ff" >boolean Test (Apple Apple) { return " green ".equals (Apple.getcolor ()); }}
Public class Implements applepredicate { //Big Apple predicate public boolean Test (Apple apple) { return apple.getweight () >; }}
Now, you can think of these criteria as different behaviors of the filter method. What you just did. These are related to the "policy design pattern", which allows you to define a set of algorithms, encapsulate them (as "policies"), and then select an algorithm at run time. Here, Applepredicate is the algorithm group, the different strategies are appleheavyweightpredicate and applegreencolorpredicate.
You need the Filterapples method to accept the Applepredicate object and do a conditional test on Apple. This is behavior parameterization: Allow methods to accept multiple behaviors (or strategies) as parameters and use them internally to accomplish different behaviors.
4. Fourth attempt: filtering based on abstract criteria
// Filter by abstract criteria Public Static List<apple> filterapples (list<apple> apples,applepredicate p) { ListNew Arraylist<>(); for (Apple apple:apples) { if(p.test (Apple)) { result.add (Apple); } } return result; }
This is used when:
list<apple> greenapple = Filterapples (apples,new applegreencolorpredicate ()); List<Apple> bigapple = filterapples (apples,new appleheavyweightpredicate ());
1. Passing Code/behavior
Here we can celebrate a little bit more flexible than the first time we tried, and it's easier to read and use! Now you can create different applepredicate objects and pass them to the Filterapples method. For example, now that the product lets you find all the red apples weighing more than 80 grams, you just need to create a class to implement Applepredicate, your code is now flexible enough to deal with any design Apple attribute needs change:
Public class Implements applepredicate { @Override publicboolean Test (Apple apple) { return "Red". Equals (Apple.getcolor ()) && apple.getweight () >; }}
Passed to the Filterapples method without modifying the internal implementation of the Filterapples method:
list<apple> redbigapple = Filterapples (apples,new appleredandbigpredicate ());
Now you've done a cool thing:The behavior of the Filterapples method depends on the code you pass through the Applepredicate object. In other words, you parameterize the behavior of the Filterapples Method!
In the previous example, the only important code is the implementation of the test method, which formally defines the new behavior of the Filterapples method. Since the Filterapples method can only accept objects, you must wrap the code in the Applepredicate object. This approach is similar to "passing code" inline, because you pass a Boolean expression through an object that implements the test method.
2. Multiple behaviors, one parameter
The advantage of the behavior parameterization is that you can separate the logic that traverses the collection from the logic of each element in the collection. This allows you to reuse the same method and give it different behaviors to achieve different purposes.
Behavioral parameterization Exercises
Write the Printapple method to implement a feature that generates a string output in a variety of ways based on Apple (for example, you can let the Printapple method only print each Apple color, or let it print what color big (small) apple)
Creating the Appleformater interface
Public Interface Appleformater { String accept (Apple a);}
Create Appleweightformater, Applecolorformater to implement interfaces
Public class Implements appleformater { @Override public String Accept (Apple a) { return ] This is a "+ a.getcolor () +" + (A.getweight () > 32? "Big": "small") + "Apple"; }}
Public class Implements appleformater { @Override public String Accept (Apple a) { Return "one" + a.getcolor () + "Apple"; }}
Then create the Printapple method to pass a different Formater object to it
Public Static void printapple (list<apple> apples,appleformater af) { for (Apple Apple:apples) { = af.accept (apple); SYSTEM.OUT.PRINTLN (output); } }
Test:
Newnew applecolorformater ());
Now you can abstract the behavior and make your code more responsive to changes in requirements, but the process is verbose because you need to declare a lot of classes that are instantiated only once.
Anonymous class
Use anonymous classes to deal with these classes that only need to be used once.
For example, using anonymous classes to add a new print style: Wow, this is a Big Apple ah?
New Appleformater () { public String Accept (Apple a) { return "Wow, this is a" + ( A.getweight () > 32? "Big": "small") + "Apple ah?" "; }});
Using anonymous classes solves the verbose problem of declaring several entity classes for an interface, but it is still unsatisfactory.
Using lambda expressions
The above code can be overridden with a lambda expression to look like this:
- "Wow, this is a" + (A.getweight () > 32? "Big": "small") + "Apple ah?" ");
Does that look more refreshing than that? More like a description of the problem itself! About Lambda will be described in the next section.
Abstracting the list type
Currently the Filterapples method is only available for Apple and can abstract the list type so that it supports bananas, oranges, integers, or string lists!
Public Interface Predicate<t> { boolean Test (T-t);}
Public static <T> list<t> filter (list<t> list, predicate<t> p) { listnew arraylist<>(); for (T e:list) { if(P.test (e)) { result.add (e); } } return result; }
Test:
List<apple> greenapple = filter (apples, apple apple), "green". Equals (Apple.getcolor ())); System.out.println (greenapple); List<Integer> evennumbers = Filter (Arrays.aslist (1,2,3,4,5,6,10), (Integer i), i%2 ==0); System.out.println (evennumbers);
Handsome, not handsome? You now find the best balance between flexibility and indirection, which is impossible before Java 8!
A real example
As you can see now, behavioral parameterization is a useful pattern that adapts easily to changing needs. This pattern encapsulates a behavior (a piece of code) and parameterize the behavior of the method by passing and using the behavior of the dress (for example, different verbs to apple).
1. Use Comparator to sort
Sorting a collection is a common task, for example, a product that wants to sort by Apple's weight. In Java 8, the list comes with a sort method (you can also use Collections.sort). The sort behavior can be parameterized with the Java.util.Comparator object, which has the following interface:
Package java.util; @FunctionalInterface Public Interface Comparator<t> { int compare (t O1, T O2);}
Therefore, you can create comparator implementations at any time, sort them by using sort methods, and use anonymous classes in ascending order of weight:
Apples.sort (new comparator<apple>() { @Override publicint Compare (Apple O1, Apple O2) { return o1.getweight (). CompareTo (O2.getweight ()) ; }});
Use lambda as follows:
Apples.sort (Apple a1,apple A2), A1.getweight (). CompareTo (A2.getweight ()));
2. Executing a code block with runnable
Threads are like lightweight processes: they execute a block of code themselves. In Java, you can use the Runnable interface to represent a block of code to execute.
Package Java.lang; @FunctionalInterface Public Interface Runnable { publicabstractvoid run ();}
Use anonymous classes to create threads that perform different behaviors:
New Thread (new Runnable () { @Override publicvoid run () { System.out.println ("t111");} );
Using lambda:
New Thread ((), System.out.println ("t2222"));
Summary:
1. Behavior parameterization, is a method to accept a number of different behaviors as parameters, and use them internally, the ability to complete different behaviors.
2. Behavioral parameterization allows code to better adapt to changing needs and reduce future workloads.
3. Passing the code means passing the new behavior as a parameter to the method, which is verbose before Java 8. Verbose code that declares many of the entity classes that are used only once for an interface can be reduced by using anonymous classes before Java 8.
The 4.java API contains many methods that can be parameterized with different behaviors, including sorting, threading, and so on.
Java 8 (1) Behavior parameterization