Java Design Mode - Strategy

Duck problem

Requirements: Write a duck project to display behavioral information (flight, barking) of all kinds of ducks (wild duck, Beijing duck, toy duck)

Traditional Solution

Write duck Abstract classes, define various methods of behavior information, the child classes are all kinds of ducks, the same behavior as the parent class is directly inherited, different behavior methods are overridden;

Duck abstract class

public abstract class Duck {

    public Duck() {}

    // Show duck information
    public abstract void display();

    public void quack() {
        System.out.println("quack");
    }

    public void swim() {
        System.out.println("A duck can swim");
    }

    public void fly() {
        System.out.println("Duck can fly");
    }
}

Subclass implementation

public class WildDuck extends Duck{
    @Override
    public void display() {
        System.out.println("This is a wild duck");
    }
}
public class PekingDuck extends Duck{
    @Override
    public void display() {
        System.out.println("This is Peking Duck");
    }

    // Peking duck can't fly and needs to be rewritten
    @Override
    public void fly() {
        System.out.println("Peking Duck Can't Fly");
    }
}
public class ToyDuck extends Duck{
    @Override
    public void display() {
        System.out.println("This is a toy duck");
    }

    @Override
    public void quack() {
        System.out.println("A toy duck cannot bark");
    }

    @Override
    public void fly() {
        System.out.println("Toy Duck Can't Fly");
    }
}

Client Call

public class Client {
    public static void main(String[] args) {
        PekingDuck pekingDuck = new PekingDuck();
        ToyDuck toyDuck = new ToyDuck();
        WildDuck wildDuck = new WildDuck();
        pekingDuck.display();
        pekingDuck.fly();
        toyDuck.display();
        toyDuck.quack();
        wildDuck.display();
        wildDuck.fly();
    }
}

Problems with Traditional Ideas

  • Modifying the parent method affects all subclasses, so the subclass overrides the parent method itself as appropriate
  • Toy ducks behave differently from their parents and need to be rewritten in many ways, which causes a lot of trouble

Strategic Mode Solution


For this case, the client is a variety of ducks, each behavior corresponds to a policy interface, and each behavior is in a specific way a policy object

Draw duck functions as policy interfaces (take flying here for example)

public interface FlyBehavior {
    // Implemented by subclasses
    void fly();
}

Policy Implementation Class

public class GoodFlyBehavior implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("High-tech flight");
    }
}
public class BadFlyBehavior implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("Flying technology is general");
    }
}
public class NoFlyBehavior implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("Will not fly");
    }
}

Duck Parents

public abstract class Duck {

    // Property, Policy Interface
    FlyBehavior flyBehavior;

    public Duck() {}

    // Show duck information
    public abstract void display();

    public void fly() {
        if (flyBehavior != null) {
            flyBehavior.fly();
        }
    }
    // The same is true for other acts
}

Ducks

public class WildDuck extends Duck{

    // Constructor passes in FlyBehavior policy object
    public WildDuck() {
        flyBehavior = new GoodFlyBehavior();
    }

    @Override
    public void display() {
        System.out.println("This is a wild duck");
    }
}
public class PekingDuck extends Duck{

    public PekingDuck() {
        flyBehavior = new BadFlyBehavior();
    }

    @Override
    public void display() {
        System.out.println("This is Peking Duck");
    }
}
public class ToyDuck extends Duck{

    public ToyDuck() {
        flyBehavior = new NoFlyBehavior();
    }
    
    @Override
    public void display() {
        System.out.println("This is a toy duck");
    }
}

Client Call

public class Client {
    public static void main(String[] args) {
        PekingDuck pekingDuck = new PekingDuck();
        pekingDuck.display();
        pekingDuck.fly();

        ToyDuck toyDuck = new ToyDuck();
        toyDuck.display();
        toyDuck.fly();

        WildDuck wildDuck = new WildDuck();
        wildDuck.display();
        wildDuck.fly();
    }
}


A toy duck can take off if needed

public abstract class Duck {

    // Property, Policy Interface
    FlyBehavior flyBehavior;

    public Duck() {}

    // Show duck information
    public abstract void display();

    public void fly() {
        if (flyBehavior != null) {
            flyBehavior.fly();
        }
    }
	// Easy to expand
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }
    // The same is true for other acts
}
public class Client {
    public static void main(String[] args) {
        ToyDuck toyDuck = new ToyDuck();
        toyDuck.setFlyBehavior(new GoodFlyBehavior());
        toyDuck.display();
        toyDuck.fly();
    }
}

Policy Mode

Basic Introduction

Basic Introduction

  • A strategy pattern defines a set of algorithms that are encapsulated so they can be replaced with each other. This pattern makes the algorithm change independent of the customer who uses the algorithm

Design principles embodied in the strategy pattern:

  • Separate changing code from changing code
  • Policy interfaces are defined for interface programming rather than specific classes
  • Combination/Aggregation, Inheritance Less, Customers Use Strategy Through Combination

Implementation method:

  • Policy interfaces act as member variables of the client, injecting subclass instance objects through polymorphism
  • Clients unify various functions through policy interfaces.Specify which policy object to use through the constructor
  • What method does the client need to pass in the subclass object of the corresponding policy interface
  • Clients can use either one or more policy objects

Understand:

  • Applications that are essentially polymorphic
  • Individual subclasses override parent methods to achieve different functions
  • Instead of overriding the parent method, the subclass now introduces different policy objects as variables, and policy interface methods are overridden by policy objects.
  • Subclasses call parent methods directly to achieve different functions by injecting different policy objects
  • To put it plainly, a policy interface, a policy object is added between the child class and the parent class
  • In this way, decoupling is implemented, and the functions of subclasses can be changed dynamically by introducing other policy objects.

Notes and Details

  • Key to strategy patterns: analyzing the changing and changing parts of a project
  • Core idea: use combination/aggregation more than inheritance;Combine with behavior classes instead of behavior inheritance and be more resilient
  • Reflects the principle of open and close, that is, close to modification, open to extension, client add behavior does not need to modify the original code, just add a policy or behavior, avoid using multiple transfer statements, if else...
  • Provides an alternative to inheritance by encapsulating the algorithm in a separate policy class, allowing you to change it independently of its context, making it easy to switch, understand, and expand
  • Note: Every policy you add adds a class. Too many policies can result in a large number of classes

JDK Source Application-sort

Write a demo

public class StrategyComparator {
    public static void main(String[] args) {
        Integer[] data = {9,1,2,8,4,3};

        // An object (anonymous internal class) that implements the policy interface
        Comparator<Integer> comparator = new Comparator<Integer>() {
            //Specify specific ways to compare policies
            @Override
            public int compare(Integer o1, Integer o2) {
                if (o1 > o2) {
                    return 1;
                } else {
                    return -1;
                }
            }
        };

        Arrays.sort(data,comparator);

        System.out.println(Arrays.toString(data));
    }
}

Determine whether sort is in ascending or descending order by returning a value of 1 or -1 from compare in the policy object comparator

Enter sort view

public static <T> void sort(T[] a, Comparator<? super T> c) {
        if (c == null) {
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);
            else
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }

Empty policy objects use default ordering, while policy objects use policy to enter legacyMergeSort

private static <T> void legacyMergeSort(T[] a, Comparator<? super T> c) {
        T[] aux = a.clone();
        if (c==null)
            mergeSort(aux, a, 0, a.length, 0);
        else
            mergeSort(aux, a, 0, a.length, 0, c);
    }

Enter mergeSort and we intercept some of the code

private static void mergeSort(Object[] src,
                                  Object[] dest,
                                  int low, int high, int off,
                                  Comparator c) {
        int length = high - low;

        // Insertion sort on smallest arrays
        if (length < INSERTIONSORT_THRESHOLD) {
            for (int i=low; i<high; i++)
                for (int j=i; j>low && c.compare(dest[j-1], dest[j])>0; j--)
                    swap(dest, j, j-1);
            return;
        }
        // ...
}

Finally, we see that the comparison sort here requires that the strategy be determined based on the return value of the compare method

We can also optimize demo, using lambda expressions, but in essence they are the same

public class StrategyComparator {
    public static void main(String[] args) {
        Integer[] data = {9,1,2,8,4,3};

        Arrays.sort(data,(o1,o2) -> {
            if (o1.compareTo(o2) > 0) {
                return 1;
            } else {
                return -1;
            }
        });

        System.out.println(Arrays.toString(data));
    }
}

Tags: Java Algorithm Design Pattern Polymorphism

Posted by sathyan on Thu, 01 Jul 2021 01:55:04 +0930