The introduction of

  • Consider a scenario where we have an integer array that we want to sort by calling the sort method of a utility class. Take a look at the following code:
public class Strategy {
    public static void main(String[] args) {
        int[] arr = {5.3.1.7.2};
        new DataSorter().sort(arr);// Call the utility class for sorting
        for(int i = 0; i < arr.length; i++){ System.out.println(arr[i]); }}}class DataSorter{// Utility class for sorting
    public void sort(int[] arr){// Call the sort method to sort, where bubble sort is used
        for(int i = arr.length - 1; i > 0; i--){
            for(int j = 0; j < i; j++){
                if(arr[j] > arr[j + 1])
                    swap(arr, j, j  + 1); }}}private void swap(int[] arr, int i, int j){
        inttemp = arr[i]; arr[i] = arr[j]; arr[j] = temp; }}Copy the code

 

The ins and outs of the Comparable interface

  • With the above code, we can easily sort an integer array, so if we now have a new requirement to sort floating-point data, what should the sorting utility class do?
  • You might be thinking, why not just add a new sort method with an argument of typefloatType, int type array sort algorithm copy again.
  • So if I were to follow up, if I were to rank a cat now, what would I do? The categories of cats are as follows
class Cat{
    private int age;// The cat's age
    private int weight;// The cat's weight

    //get/set...
}
Copy the code
  • You might answer along the lines of the original, copy the sorting algorithm, modify the method parameters, and specify the cat’s age or weight in the comparison area.
public void sort(Cat[] arr){// Take an array of cats as arguments
    for(int i = arr.length - 1; i > 0; i--){
        for(int j = 0; j < i; j++){
            if(arr[j].getAge() > arr[j + 1].getAge())// Compare according to the age of the cat
                swap(arr, j, j  + 1); }}}Copy the code
  • But if you think about it, if you continue to compare puppies, chickens, ducklings, and so on, doesn’t that sort utility class become a lot of code? In order to make the sorting algorithm a little bit more reusable, we want the sorting toolsort()Method can sort any object that calls it.
  • You might think: to be able to sort any object, thesort()Change the parameter of the method toObjectThe type will be ready soon. That’s the right direction, but the problem is, when you get twoObjectType objects, by what rules should we compare them?
  • At this point it is natural to expect that the object that calls the utility class to sort will have its ownComparative law“, so that when sorting can directly call the object’s sorting algorithm to sort.
  • Let’s abstract the law of comparisonComparableInterface, any class that you want to compare is implementedComparableInterface, and define your own comparison rules, which areCompareTo()Methods.
  • This way we can implement it directly when we package the toolComparableInterface objects, don’t worry about the details of the comparison.
public class Strategy {
public class Strategy {
    public static void main(String[] args) {
// Integer[] arr = {5, 3, 1, 7, 2}; // Notice that int is changed to Integer, which is a subclass of Object
        Cat[] arr = {new Cat(3.3), new Cat(1.1), new Cat(5.5)};
        DataSorter ds = new DataSorter();
        ds.sort(arr);
        for(int i = 0; i < arr.length; i++){ System.out.println(arr[i]); }}}}class DataSorter{// Utility class for sorting
    public void sort(Object[] arr){// The parameter type is Object
        for(int i = arr.length - 1; i > 0; i--){
            for(int j = 0; j < i; j++){
                Comparable c1 = (Comparable) arr[j];// Change to type Comparable first
                Comparable c2 = (Comparable) arr[j + 1];
                if(c1.CompareTo(c2) == 1)// Call CompareTo() for comparison, regardless of implementation
                    swap(arr, j, j  + 1); }}}private void swap(Object[] arr, int i, int j){ Object temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; }}class Cat implements Comparable{
    private int age;
    private int weight;

    @Override
    public int CompareTo(Object o) {
        if(o instanceof Cat){// Check whether the passed object is a Cat object
            Cat c = (Cat) o;
            if(this.age > c.age) return 1;
            else if (this.age < c.age) return -1;
            else return 0;
        }
        throw null == o ? new NullPointerException() : new ClassCastException();
    }

    // get / set ...
    //toString() ...
}

interface Comparable{
    public int CompareTo(Object o);
}
Copy the code

 

Introduce the Comparator interface

  • I believe I read the aboveComparableThe interface, you feel like the whole design is better, but there are still bugs. We are inCatOf the classCompareTo()In the method, the cat comparison strategy is written dead, now we compare the size of the cat by age, if one day we want to compare the size of the cat by weight, and have to modify the source code. Is there a more scalable design?
  • We can let the user define a comparator class in which objects can be sized according to the comparator specified by the user.
  • The whole logic is: if this object needs to be compared, it must be implementedComparableInterface, but how exactly does it compare, by whatComparatorComparator for comparison.
  • Of course, there is polymorphism, so we need to define a comparator interface firstComparatorThe user’s comparator needs to be implementedComparatorInterface, the following code:
public class Strategy {
    public static void main(String[] args) {
        Cat[] arr = {new Cat(3.3), new Cat(1.1), new Cat(5.5)};
        DataSorter ds = new DataSorter();
        ds.sort(arr);
        for(int i = 0; i < arr.length; i++){ System.out.println(arr[i]); }}}class DataSorter{// Utility class for sorting
    public void sort(Object[] arr){// The parameter type is Object
        for(int i = arr.length - 1; i > 0; i--){
            for(int j = 0; j < i; j++){
                Comparable c1 = (Comparable) arr[j];// Change to type Comparable first
                Comparable c2 = (Comparable) arr[j + 1];
                if(c1.CompareTo(c2) == 1)// The rules that have been converted behind them to use the definitions of the Comparator
                    swap(arr, j, j  + 1); }}}private void swap(Object[] arr, int i, int j){ Object temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; }}class Cat implements Comparable{
    private int age;
    private int weight;
    private Comparator comparator = new CatAgeComparator();// The age comparator is held by default

    @Override
    public int CompareTo(Object o) {
        return comparator.Compare(this, o);// Call the comparator to compare instead of writing the comparison rule directly here
    }

    // get / set / toString ...
}

interface Comparable{
    public int CompareTo(Object o);
}

interface Comparator{
    public int Compare(Object o1, Object o2);
}

// A user defined comparator that compares the size of the cat according to its age
class CatAgeComparator implements Comparator{
    @Override
    public int Compare(Object o1, Object o2) {
        Cat c1 = (Cat) o1;
        Cat c2 = (Cat) o2;
        if(c1.getAge() > c2.getAge()) return 1;
        else if(c1.getAge() < c2.getAge()) return -1;
        else return 0; }}// A comparator that compares the size of the cat according to its weight
class CatWeightComparator implements Comparator{
    @Override
    public int Compare(Object o1, Object o2) {
        Cat c1 = (Cat) o1;
        Cat c2 = (Cat) o2;
        if(c1.getWeight() > c2.getWeight()) return 1;
        else if(c1.getWeight() < c2.getWeight()) return -1;
        else return 0; }}Copy the code

 

What are strategic patterns?

  • In the example above, we defined it ourselvesComparableThe interface andComparatorInterface, in fact, both of these interfaces are Java built-in, through the above code example, presumably you should also know why there are two interfaces.
  • Actually,ComparableThe definition is a comparative strategy, the strategy here you can think of as a function, but the strategy is there, we need to have a specific strategy implementation, and then there isComparatorInterface.

 

  • Here’s another example for you to understand.
  • Now there is a tank mini-game, tanks need to be able to fire shells, so we can think of firing shells as a strategy, but in terms of what shells to send, this can be achieved by specific strategies.
  • Check out the tank game on GitHub
  • First, define the strategy of firing a cannon ball
public interface Fire {
    public void fire(a);// The firing strategy
}
Copy the code
  • In order to realize the strategy of firing shells, define the concrete implementation of the strategy, that is, define the action of firing shells
public interface FireAction {
    public void fireAction(Tank tank);
}
Copy the code
  • Tanks that want to send shells have to do soFire()Interface, and the tank has the action of firing shells, as for the specific implementation of the action, here gives the default action of firing only one shell.
public class Tank implements TankHitListener.Fire {
    // omit various attribute methods...
    private FireAction fireAction = new NormalFireAction();// The default action is to fire only one shell
    
    @Override
    public void fire(a) {
        fireAction.fireAction(this);
    }
    
    / /...
Copy the code

What are the benefits of using the policy pattern?

  • In the tank game above, for example, when firing shells is defined as a strategy, not only tanks can fire shells, but if there is a mechanism in the game, it can be implementedfire()Interface, gaining the ability to fire shells.
  • And after defining the strategy, we can give different implementation methods according to the strategy. For example, the action of tank firing shells is to fire only one shell at a time, while the mechanism is to fire one shell in eight directions at a time. Very flexible.
  • The end of the