Strategy mode is actually very common in the actual development process, so this chapter also extends from the original requirements to the design mode, how to better implement it.
1. Definition
Encapsulate the behavior interface, implement the algorithm family, put the behavior interface object in the parent class, and set the behavior object in the child class. The behavior of a class or its algorithm can be changed at run time. This type of design pattern belongs to behavior pattern.
The definition is always vague. In vernacular, We call every thing we have to do [behavior], each behavior should be abstracted into an interface and decoupled from the users of these behaviors. For example, duck, an abstract parent class, has many behavior methods. Ducks can swim, bark and have many abilities; each different duck has to inherit the abstract duck parent class. Each duck's cry is different and needs to call the parent class Can be rewritten. It seems that there is no problem. If additional requirements are added, such as ducks flying, it is likely that all subclasses will have to rewrite flying, but not all ducks can fly. Once there are more kinds of ducks, the changes will be great. The effect we need is that all ducks should not be affected.
Since it's outrageous, let's analyze whether the changing things are some characteristics? For example, not all ducks can fly and not all ducks can dance (such as Donald Duck). Let's assume that every duck can swim. Then take out individuality and keep the commonness unchanged, that is, abstraction is commonness and interface is characteristic.
Here comes the soul painter again:
2. Case explanation
I won't talk about the components. The above figure shows which components officially enter the requirements:
Product Manager: Lao Wang, do me a function. I want two ducks, black duck and Red duck. These two ducks can bark, fly and swim. The difference is that the colors of black duck and Red duck are different (isn't this nonsense).
At this time, we use OOP (object-oriented programming) to define a duck parent class.
/** * @Description: Design an abstract class * @title: Duck * @Author Star_Chen * @Date: 2021/12/21 22:36 * @Version 1.0 */ public abstract class Duck { public Duck() { } /** * @Date: 2021/12/21 22:39 * @Description: Abstract self introduction color method enables subclass implementation */ public abstract void perform(); /** * @Date: 2021/12/21 22:39 * @Description: This abstract parent class implements its own swimming methods, and its subclasses can also implement their own methods */ public void swim(){ System.out.println("I'm a duck. I can swim~"); } /** * @Date: 2021/12/21 22:46 * @Description: This abstract parent class implements the methods called by itself, and the subclass can also implement them by itself */ public void quack(){ System.out.println("I'm a duck, I can call quack quack!"); } }
A Red duck
/** * @Description: Red duck * @title: RedDuck * @Author Star_Chen * @Date: 2021/12/21 22:40 * @Version 1.0 */ public class RedDuck extends Duck { @Override public void perform() { System.out.println("I'm a Red duck!"); } }
Another black duck
/** * @Description: Black duck * @title: BlackDuck * @Author Star_Chen * @Date: 2021/12/21 22:41 * @Version 1.0 */ public class BlackDuck extends Duck { @Override public void perform() { System.out.println("I'm a black duck!"); } }
Call the Main function
/** * @Description: * @title: DuckMain * @Author Star_Chen * @Date: 2021/12/21 22:44 * @Version 1.0 */ public class DuckMain { public static void main(String[] args) { RedDuck redDuck = new RedDuck(); BlackDuck blackDuck = new BlackDuck(); redDuck.quack(); redDuck.perform(); redDuck.swim(); System.out.println("<==========================>"); blackDuck.quack(); blackDuck.perform(); blackDuck.swim(); } }
The output is simple
I'm a duck, I can call quack quack! I'm a Red duck! I'm a duck. I can swim~ <==========================> I'm a duck, I can call quack quack! I'm a black duck! I'm a duck. I can swim~
At this time, the product manager said, by the way, give me two more ducks and let the ducks fly, but I only let the black ducks fly and xxx ducks fly. By the way, I also want different ducks to call differently. The Red duck geigeigei and the black duck give me giaogiao
Think about it. If you write a fly method in the parent class and define it as an abstract method, all ducks that inherit the parent class must add a fly method, but not all ducks can fly, which is contrary to logic. If you do not define it as an abstract method, an ordinary method defined as a parent class, and all ducks of a child class, you must override the fly of the parent class or call the fly of the parent class in order to implement it. However, in practice, such as Beijing roast duck, there is no concept of flying. How can you make a child class have such a method? Obviously, this is impossible
A pit dug by the parent class and filled by each child class, which increases the workload and complexity O(N^2). Not a good design. At this time, we should consider the disadvantages of Kazakhstan's inheritance. This problem can be solved by using the interface. (idea: inheritance is to realize commonness and reduce code repetition. Interface is to realize characteristics.)
We use the strategic model to do it
Analysis:
1. Project change and unchanged part, extract the changed part and abstract it into interface + implementation; (abstraction is commonness and interface is characteristic)
2. Which functions will change according to new requirements? Call, flight
Define two changing behavioral interfaces
FlyBehavior and QuackBehavior
Define the behavior of flying
/** * @Description: * @title: FlyBehavior * @Author Star_Chen * @Date: 2021/12/21 23:53 * @Version 1.0 */ public interface FlyBehavior { /** * @Date: 2021/12/21 23:54 * @Description: Flying behavior */ void fly(); }
Define behavior called
/** * @Description: * @title: QuackBehavior * @Author Star_Chen * @Date: 2021/12/22 0:00 * @Version 1.0 */ public interface QuackBehavior { /** * @Date: 2021/12/22 0:01 * @Description: Called behavior */ void quack(); }
Then we need to adjust the Duck parent class. As long as it involves personality, such as fly and quack, we need to change it by behavior.
/** * @Description: Design an abstract class * @title: Duck * @Author Star_Chen * @Date: 2021/12/21 22:36 * @Version 1.0 */ public abstract class Duck { //Have abstract behavior FlyBehavior flyBehavior; QuackBehavior quackBehavior; /** * @Date: 2021/12/22 0:07 * @Description: When instantiating an object, it can dynamically change the behavior of the object (more flexible than inheritance) */ public void setFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; } /** * @Date: 2021/12/22 0:07 * @Description: When instantiating an object, it can dynamically change the behavior of the object (more flexible than inheritance) */ public void setQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; } /** * @Date: 2021/12/21 22:46 * @Description: This abstract parent class implements the methods called by itself, and the subclass can also implement them by itself */ public void quack(){ // System.out.println("I'm a duck, I'll call quack quack!"); quackBehavior.quack(); } public void fly(){ flyBehavior.fly(); } /** * @Date: 2021/12/21 22:39 * @Description: Abstract self introduction color method enables subclass implementation */ public abstract void perform(); /** * @Date: 2021/12/21 22:39 * @Description: This abstract parent class implements its own swimming methods, and its subclasses can also implement their own methods */ public void swim(){ System.out.println("I'm a duck. I can swim~"); } }
Implementation class of behavior
/** * @Description: giaogiao My name is * @title: GiaoQuackBehavior * @Author Star_Chen * @Date: 2021/12/22 0:01 * @Version 1.0 */ public class GiaoQuackBehavior implements QuackBehavior { @Override public void quack() { System.out.println("Give it to me giaogiao What a cry!"); } } /** * @Description: geigei My name is * @title: UglyQuackBehavior * @Author Star_Chen * @Date: 2021/12/22 0:01 * @Version 1.0 */ public class UglyQuackBehavior implements QuackBehavior { @Override public void quack() { System.out.println("geigei What a cry!"); } }
Implementation class for flight
/** * @Description: Fly low * @title: LowFlyBehavior * @Author Star_Chen * @Date: 2021/12/21 23:55 * @Version 1.0 */ public class LowFlyBehavior implements FlyBehavior { @Override public void fly() { System.out.println("Ducks can fly low!"); } } /** * @Description: Goofy * @title: HighFlyBehavior * @Author Star_Chen * @Date: 2021/12/21 23:54 * @Version 1.0 */ public class HighFlyBehavior implements FlyBehavior { @Override public void fly() { System.out.println("Ducks can fly high!"); } }
Let's simulate the duck main function or implement the newly added requirements above:
The Red duck is called geigeigei, and the black duck is called to me
Red ducks fly high and black ducks fly low
In terms of calling, we write as follows:
/** * @Description: * @title: DuckMain * @Author Star_Chen * @Date: 2021/12/21 22:44 * @Version 1.0 */ public class DuckMain { public static void main(String[] args) { //The parent class is Duck, which shields the difference of superclasses Duck redDuck = new RedDuck(); Duck blackDuck = new BlackDuck(); redDuck.perform(); redDuck.fly(); System.out.println("<==========================>"); blackDuck.perform(); blackDuck.quack(); } }
Output results:
I'm a Red duck! Ducks can fly high! <==========================> I'm a black duck! Give it to me giaogiao What a cry!
If I want to override the behavior when instantiating a custom object, I can implement it through setBehavior.
/** * @Description: * @title: DuckMain * @Author Star_Chen * @Date: 2021/12/21 22:44 * @Version 1.0 */ public class DuckMain { public static void main(String[] args) { //The parent class is Duck, which shields the difference of superclasses Duck redDuck = new RedDuck(); Duck blackDuck = new BlackDuck(); redDuck.perform(); redDuck.setFlyBehavior(new FlyBehavior(){ @Override public void fly() { System.out.println("Red duck, I won't fly"); } }); redDuck.fly(); System.out.println("<==========================>"); blackDuck.perform(); blackDuck.setQuackBehavior(new QuackBehavior() { @Override public void quack() { System.out.println("Black duck, I won't bark"); } }); blackDuck.quack(); } }
The above code is the basic knowledge in java. Use the strategy mode in the design mode. Extract the changed part and turn it into interface + implementation. In the main method, the parent class is the Dock base class, which is to shield the difference of the superclass Dock of the subclass. The SetQuackBehavoir() method in Duck class allows the instantiated object to flexibly change the behavior of the object. For example, the mallard uses the SetQuackBehavoir() method and customizes its own quck() method. Because not every Duck can bark, it is the characteristics of the current Duck.
3. Summary
We can imagine that a duck can have many subclasses. If you write all the methods in the parent class. Subclasses inherit it. For different subclasses, some methods violate logic and become redundant (they should not appear in subclasses).
Is there no defect? Of course, all these behaviors must be known at the calling end, otherwise we can't determine the result of the calling behavior on any occasion. For many scenario requirements, we don't know exactly what the next requirement is, that is, we don't know how many behavior policy classes will exist, because each behavior policy will have a class. If it is called in multiple places, You must know all the policy scenarios, and maintenance is not very convenient.
In the next chapter, we talk about the decorator model.