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)); } }