Java design pattern [22] - policy pattern

Posted by discobean on Fri, 31 Dec 2021 08:35:50 +0100

Basic introduction
1. In the Strategy Pattern, algorithm families (policy groups) are defined and encapsulated respectively so that they can be replaced with each other. This pattern makes the change of the algorithm independent of the customers using the algorithm.
2. This algorithm embodies several design principles,
First, separate the changed code from the unchanged code;
Second, programming for interfaces rather than specific classes (policy interfaces are defined);
Third, use more combination / aggregation and less inheritance (customers use strategies through combination).

Schematic class diagram of policy pattern

 

example

The specific requirements of the duck project are as follows: there are all kinds of ducks (such as wild duck, Peking duck, water duck, etc., and ducks have all kinds of behaviors, such as barking, flying, etc.).
 

public abstract class Duck {

	// Properties, policy interface
	FlyBehavior flyBehavior;
	// Other attributes < - > policy interface
	QuackBehavior quackBehavior;

	public Duck() {

	}

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

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

	public void swim() {
		System.out.println("Ducks can swim~~");
	}

	public void fly() {

		// improvement
		if (flyBehavior != null) {
			flyBehavior.fly();
		}
	}

	public void setFlyBehavior(FlyBehavior flyBehavior) {
		this.flyBehavior = flyBehavior;
	}

	public void setQuackBehavior(QuackBehavior quackBehavior) {
		this.quackBehavior = quackBehavior;
	}

}

public class PekingDuck extends Duck {

	// If Beijing ducks can fly, but their flying skills are average
	public PekingDuck() {
		flyBehavior = new BadFlyBehavior();
	}

	@Override
	public void display() {
		System.out.println("~~Peking duck~~~");
	}

}

public class ToyDuck extends Duck {

	public ToyDuck() {
		flyBehavior = new NoFlyBehavior();
	}

	@Override
	public void display() {
		System.out.println("Toy duck");
	}

	// All methods of the parent class need to be overridden
	public void quack() {
		System.out.println("Toy ducks can't bark~~");
	}

	public void swim() {
		System.out.println("Toy ducks can't swim~~");
	}

}

public class WildDuck extends Duck {

	// Constructor, object passed in FlyBehavor
	public WildDuck() {
		flyBehavior = new GoodFlyBehavior();
	}

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

}

public interface FlyBehavior {

	void fly(); // Subclass concrete implementation

}

public class GoodFlyBehavior implements FlyBehavior {

	@Override
	public void fly() {
		System.out.println(" Superb flying skills ~~~");
	}

}

public class BadFlyBehavior implements FlyBehavior {

	@Override
	public void fly() {
		System.out.println(" General flying skills ");
	}

}

public class NoFlyBehavior implements FlyBehavior {

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

}

public interface QuackBehavior {

	void quack();// Subclass implementation

}

public class Client {

	public static void main(String[] args) {
		WildDuck wildDuck = new WildDuck();
		wildDuck.fly();//

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

		PekingDuck pekingDuck = new PekingDuck();
		pekingDuck.fly();

		// Dynamically change the behavior of an object, Peking duck can't fly
		pekingDuck.setFlyBehavior(new NoFlyBehavior());
		System.out.println("The actual flying ability of Peking Duck");
		pekingDuck.fly();
	}

}

Source code analysis of policy pattern in JDK arrays application

public class Strategy {

	public static void main(String[] args) {
		// array
		Integer[] data = { 9, 1, 2, 8, 4, 3 };
		// Realize descending sorting, return - 1 on the left, 1 on the right, and 0 remains unchanged

		// explain
		// 1. Implement Comparator interface (policy interface), anonymous class object new Comparator < integer > () {..}
		// 2. Object new comparator < integer > () {..} Is the object that implements the policy interface
		// 3. public int compare(Integer o1, Integer o2) {} specifies the specific processing method
		Comparator<Integer> comparator = new Comparator<Integer>() {
			public int compare(Integer o1, Integer o2) {
				if (o1 > o2) {
					return -1;
				} else {
					return 1;
				}
			};
		};

		// explain
		/*
		public static <T> void sort(T[] a, Comparator<? super T> c) {
			if (c == null) {
				sort(a); //Default method
			} else { 
				if (LegacyMergeSort.userRequested)
					legacyMergeSort(a, c); //Using Policy Object c
				else
					// Using Policy Object c
					TimSort.sort(a, 0, a.length, c, null, 0, 0);
			}
		}
		*/

		// Mode 1
		Arrays.sort(data, comparator);

		System.out.println(Arrays.toString(data)); // Descending sort

		// Mode 2 - implement the policy pattern simultaneously with lambda expressions
		Integer[] data2 = { 19, 11, 12, 18, 14, 13 };

		Arrays.sort(data2, (var1, var2) -> {
			if (var1.compareTo(var2) > 0) {
				return -1;
			} else {
				return 1;
			}
		});

		System.out.println("data2=" + Arrays.toString(data2));

	}

}

Considerations and details of policy mode

  1. The key of the strategy model is to analyze the changing part and the unchanged part of the project.
  2. The core idea of the strategy model is: use more combination / aggregation and less inheritance; Use behavior class composition instead of behavior inheritance. More flexible.
  3. It embodies the principle of "close to modification and open to extension". The client can add behavior without modifying the original code, just add a strategy (or behavior), avoiding the use of multiple transfer statements (if.. else, if.. else).
  4. It provides a way to replace the inheritance relationship: the policy pattern encapsulates the algorithm in an independent Strategy class, so that you can change it independently of its Context, making it easy to switch, understand and expand.
  5. It should be noted that every time you add a policy, you need to add a class. When there are too many policies, the number of classes will be huge.

Topics: Java