Click “like” to see, form a habit, the public account search [dime technology] pay attention to more original technical articles. This article has been included in GitHub org_Hejianhui /JavaStudy.

preface

  • 23 design modes for shorthand
  • The singleton pattern
  • Factory Method pattern
  • Abstract Factory pattern
  • The Builder/Builder pattern
  • Prototype mode
  • Flyweight mode
  • The facade pattern
  • Adapter mode
  • Decorator pattern
  • Observer mode
  • Updates continue at……

Here are 23 design patterns to memorize quicklyThe strategy patternRelated content.

The schema definition

A family of algorithms is defined and encapsulated so that they can be replaced with each other, and this pattern changes independently of the users of the algorithm.

The strategy pattern embodies several design principles: (1) Separate the changing code from the unchanging code; ② Programming for interfaces rather than concrete classes; ③ Use composition/aggregation more than inheritance. In policy mode, Context uses policy through aggregation.

Problem solved

In cases where multiple algorithms are similar, use if… Else is complex and difficult to maintain, using policy patterns to decouple the responsibility of the algorithm from itself.

Patterns of

To compose (a role). role
Abstract Policy Roles (Strategy) Defines a common interface that is implemented in different ways by different algorithms, and is used by environment actors to invoke different algorithms, typically implemented using interfaces or abstract classes.
ConcreteStrategy roles The interface defined by abstract policy is implemented to provide concrete algorithm implementation.
Environment (Context) An application that holds a policy class and is eventually called by the client

Example is given to illustrate

“Plants vs. Zombies” is a game many people have played, there are a variety of different plants and zombies. Different plants and zombies have different characteristics. If you’re developing a game like this, the game starts out simple, with just two types of zombies: normal zombies and flag-bearer zombies.

The first edition

type appearance mobile attack
Ordinary zombie ordinary Moving in one direction bite
Flag bearer zombie Normal + carry flag Moving in one direction bite

Step 1: Define abstract policy roles, abstract classes

abstract class AbstractZombie{

    public abstract void display(a);

    public void attack(a){
        System.out.println("Bite");
    }

    public void move(a){
        System.out.println("Move one step at a time."); }}Copy the code

Step 2: Define specific policy roles, common zombies

class NormalZombie extends AbstractZombie{

    @Override
    public void display(a) {
        System.out.println("I'm a zombie."); }}Copy the code

Step 3: Define specific policy roles, flag-bearer zombies

class FlagZombie extends AbstractZombie{

    @Override
    public void display(a) {
        System.out.println("I'm a flag-bearer zombie."); }}Copy the code

Step 4: Test

public class StrategyPattern {

    public static void main(String[] args) {
        AbstractZombie normalZombie = new NormalZombie();
        AbstractZombie flagZombie = new FlagZombie();

        flagZombie.display();
        flagZombie.move();
        flagZombie.attack();
        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); normalZombie.display(); normalZombie.move(); normalZombie.attack(); }}Copy the code

Output result:

I am a flagman zombie step by step moving bite --------------- I am a normal zombie step by step moving biteCopy the code

Perfect! The game is ready to go live.

But after a while, you find that the game you are developing is getting less and less popular. When you open the comments, they all say that the game has too few zombie types, and after playing it a few times, it becomes boring. That’s easy. Add a few zombies, and you have version two

The second edition

type appearance mobile attack
Ordinary zombie ordinary Moving in one direction bite
Flag bearer zombie Normal + carry flag Moving in one direction bite
Big head zombie Big head Moving in one direction Head against
Gypsum zombie Gypsum is installed A limp weapons
XXX zombies . . .

That’s easy. I’ll just write two more zombie classes, and then rewrite a method implementation that’s not the same as an AbstractZombie class.

Step 1: Define a specific policy role, the big-head zombie

class BigZombie extends AbstractZombie {
    @Override
    public void display(a) {
        System.out.println("I'm a big-headed zombie.");
    }

    @Override
    public void attack(a) {
        System.out.println("Head"); }}Copy the code

Step 2: Define the specific policy character, gypsum Zombie

class GypsumZombie extends AbstractZombie{

    @Override
    public void display(a) {
        System.out.println("I'm a gypsum zombie.");
    }

    @Override
    public void move(a) {
        System.out.println("Limping.");
    }

    @Override
    public void attack(a) {
        System.out.println("Arms"); }}Copy the code

OK! Version 2 comes online!

It didn’t take long for users to get tired of it, users got tired of it, and they added zombies. But it’s a good thing you’ve got it all figured out. It’s all about inheritance.

But it’s just brainless and zombies, and there are a bunch of bugs that users tease about your game: you zombies don’t stop when they meet obstacles, they should stop moving when they meet plants, and start attacking. So the behavior of these zombies is different in different situations. What can you do? You’ve already written a bunch of zombie classes. This is when you think of the open closed principle: open for extensions, closed for modifications. Looks like your code needs to be refactored.

The third edition

Zombies move and attack in different ways, and should be able to change dynamically. First extract these two behaviors as interfaces.

Step 1: Define the mobile behavior interface

interface MoveBehavior {
    void move(a);
}
Copy the code

Step 2: Define the attack behavior interface

interface AttackBehavior {
    void attack(a);
}
Copy the code

Step 3: Define abstract policy roles, abstract classes

abstract class AbstractZombie {
    MoveBehavior moveBehavior;
    AttackBehavior attackBehavior;

    public AbstractZombie(MoveBehavior moveBehavior, AttackBehavior attackBehavior) {
        this.moveBehavior = moveBehavior;
        this.attackBehavior = attackBehavior;
    }

    abstract void display(a);

    void move(a) {
        moveBehavior.move();
    }

    void attack(a) {
        attackBehavior.attack();
    }

    public void setAttackBehavior(AttackBehavior attackBehavior) {
        this.attackBehavior = attackBehavior;
    }

    public AttackBehavior getAttackBehavior(a) {
        return attackBehavior;
    }

    public void setMoveBehavior(MoveBehavior moveBehavior) {
        this.moveBehavior = moveBehavior;
    }

    public MoveBehavior getMoveBehavior(a) {
        returnmoveBehavior; }}Copy the code

Step 4: Define various zombie subclasses

class NormalZombie extends AbstractZombie {
    public NormalZombie(MoveBehavior moveBehavior, AttackBehavior attackBehavior) {
        super(moveBehavior, attackBehavior);
    }

    @Override
    void display(a) {
        System.out.println("I'm a zombie."); }}class FlagZombie extends AbstractZombie {
    public FlagZombie(MoveBehavior moveBehavior, AttackBehavior attackBehavior) {
        super(moveBehavior, attackBehavior);
    }

    @Override
    void display(a) {
        System.out.println("I'm a flag-bearer zombie."); }}class BigZombie extends AbstractZombie {
    public BigZombie(MoveBehavior moveBehavior, AttackBehavior attackBehavior) {
        super(moveBehavior, attackBehavior);
    }

    @Override
    void display(a) {
        System.out.println("I'm a big-headed zombie."); }}class GypsumZombie extends AbstractZombie {
    public GypsumZombie(MoveBehavior moveBehavior, AttackBehavior attackBehavior) {
        super(moveBehavior, attackBehavior);
    }

    @Override
    void display(a) {
        System.out.println("I'm a gypsum zombie."); }}Copy the code

Step 5: Define the movement behavior subclass

class StepByStepMove implements MoveBehavior {
    @Override
    public void move(a) {
        System.out.println("Move one step at a time."); }}class LameMove implements MoveBehavior {
    @Override
    public void move(a) {
        System.out.println("Limping."); }}Copy the code

Step 6: Define the attack behavior subclass

class BiteAttack implements AttackBehavior{
    @Override
    public void attack(a) {
        System.out.println("Bite"); }}class HeadAttack implements AttackBehavior{
    @Override
    public void attack(a) {
        System.out.println("Head"); }}class ArmsAttack implements AttackBehavior{
    @Override
    public void attack(a) {
        System.out.println("Arms"); }}Copy the code

Testing:

public class StrategyPattern {
    public static void main(String[] args) {
        // Normal zombie
        NormalZombie normalZombie = new NormalZombie(new StepByStepMove(), new BiteAttack());
        normalZombie.display();
        normalZombie.move();
        normalZombie.attack();
        System.out.println("-- -- -- -- -- -- -- -- -- -- -");
        // Flagman zombie
        FlagZombie flagZombie = new FlagZombie(new StepByStepMove(), new BiteAttack());
        flagZombie.display();
        flagZombie.move();
        flagZombie.attack();
        System.out.println("-- -- -- -- -- -- -- -- -- -- -");
        // Big head zombie
        BigZombie bigZombie = new BigZombie(new StepByStepMove(), new HeadAttack());
        bigZombie.display();
        bigZombie.move();
        bigZombie.attack();
        System.out.println("-- -- -- -- -- -- -- -- -- -- -");
        // Cast zombie
        GypsumZombie gypsumZombie = new GypsumZombie(new LameMove(), new BiteAttack());
        gypsumZombie.display();
        gypsumZombie.move();
        // If gypsum zombie encounters the first plant
        System.out.println("I met my first plant.");
        gypsumZombie.setAttackBehavior(newArmsAttack()); gypsumZombie.move(); gypsumZombie.attack(); }}Copy the code

Execution Result:

I'm a normal zombie step by step move bite ----------- I'm a flagman zombie step by step move bite ----------- I'm a big head zombie step by step move head bump ----------- I'm a plaster zombie limping I met the first plant limping weaponCopy the code

advantages

  1. Multi-conditional statements are difficult to maintain, and using policy patterns can avoid them.
  2. The policy pattern provides a family of algorithms that can be reused, and inheritance can be used appropriately to move the common code of the algorithm family into the parent class to avoid duplicate code.
  3. The policy pattern can provide different implementations of the same behavior, and customers can choose different ones based on different time or space requirements.
  4. The policy pattern provides perfect support for the open closed principle, allowing for the flexibility of adding new algorithms without modifying the original code.
  5. The policy pattern separates the use of the algorithm into the environment class and the implementation of the algorithm into the concrete policy class.

disadvantages

  1. The client must understand the differences between all the policy algorithms in order to select the right algorithm class at the right time.
  2. The policy pattern creates many policy classes.

Application scenarios

  1. Use this strategy when you have many similar classes, but they perform certain behaviors differently.

  2. Use this pattern to isolate the business logic of a class from the implementation details of algorithms that may be less important in a logical context;

  3. Use this mode when your class has a large number of conditional operators and switches between variants of the same algorithm.

Application in source code

#JDK
java.util.Comparator
java.util.concurrent.ThreadPoolExecutor

#Spring
org.springframework.beans.factory.support.InstantiationStrategy
......
Copy the code

The Comparator Comparator

In Java’s collections framework, you often pass in a Comparator for sorting, which uses the policy pattern.

Let’s look at a case

Define a Person class

class Person {

    int age;
    int height;

    public Person(int age, int height) {
        this.age = age;
        this.height = height;
    }

    public int getAge(a) {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getHeight(a) {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    @Override
    public String toString(a) {
        return "Person{" +
                "age=" + age +
                ", height=" + height +
                '} '; }}Copy the code

We’ve implemented the Comparable compareTo method of the Comparable interface, but the logic is still the same. The compareTo method is always sorted by age, so if you want to sort by height, you have to change the compareTo method in Person.

So we define two comparators:

// Sort by age
class SortByAge implements Comparator<Person> {

    @Override
    public int compare(Person o1, Person o2) {
        if (o1.getAge() > o2.getAge()) {
            return 1;
        } else if (o1.getAge() < o2.getAge()) {
            return -1;
        }
        return 0; }}// Strategy 2 Sort by height
class SortByHeight implements Comparator<Person> {

    @Override
    public int compare(Person o1, Person o2) {
        if (o1.getHeight() > o2.getHeight()) {
            return 1;
        } else if (o1.getHeight() < o2.getHeight()) {
            return -1;
        }
        return 0; }}Copy the code

Testing:

public class ComparatorTest {
    public static void main(String[] args) {
        Person[] persons =
                new Person[]{new Person(10.111), new Person(18.99), new Person(15.122)};

        Arrays.sort(persons, new SortByHeight());

        print(persons);

    }

    static void print(Person[] array) {
        for (int i = 0; i < array.length; i++) { System.out.println(array[i]); }}}Copy the code

Print result:

Person{age=18, height=99}
Person{age=10, height=111}
Person{age=15, height=122}
Copy the code

Here Arrays is the Context role, Comparator is the abstract Strategy, and the two Comparator implementations are concrete implementation strategies

Reject policy in ThreadPoolExecutor

When creating a thread pool, a reject policy is passed in, and when creating a new thread causes the number of currently running threads to exceed maximumPoolSize, the passed reject policy is used. This is also a strategic pattern.

  • AbortPolicy: Directly throws an exception
  • CallerRunsPolicy: After a rejected task is added, the thread of the current thread pool is called to execute the rejected task
  • DiscardPolicy: Discards the device without processing it
  • DiscardOldestPolicy: When a task is rejected, the system discards the oldest task in the task queue and adds the new task to the queue

Bean instantiation strategy in Spring

Interface InstantiationStrategy interface class is instantiated strategy, it defines three instantiation interface, then SimpleInstantiationStrategy implements this strategy, it is mainly do some simple bean work according to the instance constructor, Then CglibSubclassingInstantiationStrategy inherited SimpleInstantiationStrategy added method injection way according to the additional generated proxy class instantiation method.

In AbstractAutowireCapableBeanFactory management the strategy of an object, the default is CglibSubclassingInstantiationStrategy strategy, The runtime can change the instantiation strategy through the setInstantiationStrategy if you write your own individual strategy.

PS: The above code is submitted to Github: github.com/Niuh-Study/…

GitHub Org_Hejianhui /JavaStudy GitHub Hejianhui /JavaStudy