Design mode - template mode

Posted by chelerblondi on Wed, 26 Jan 2022 02:23:45 +0100

Template mode: it belongs to behavioral design mode. It is one of the more commonly used and simpler design modes. Although you may not know how to write template mode, you may have used it long ago.
Let's start with a definition:
Template mode: define the algorithm skeleton in an operation, and delay some steps of the algorithm to the subclass, so that the subclass can redefine some specific steps of the algorithm without changing the structure of the algorithm.
To put it bluntly, I define some behavior rules in the abstract parent class and specify the execution order of these behavior rules. As a subclass, you only need to implement the specific content of the behavior rules I define according to your own situation.
Take a simple example:
I stipulate that before going to bed at night, you should complete the following things: lying in bed, brushing your teeth and changing into pajamas. The order of execution is brushing your teeth - > changing your pajamas - > lying in bed, and everyone just needs to do these things according to their actual situation, just like

  • Someone used black toothpaste and ordinary toothbrush, then put on silk pajamas and lay on his Simmons mattress
  • Someone used crest + electric toothpaste, then put on the emperor's new clothes and lay on his floor

OK, here is a specific code example:
Now, as the boss of the Engineering Department of the music box listed company, you have received the task from your superior and said that the customer wants to make the music box!!!
And one is two. At this time, you look disdainful( ˉ ▽  ̄ ~) cut ~ ~ only two, so easy.
Drag a music box first. Standard design instructions

Original way

public abstract class MusicBox {

    //Turn on the switch
    abstract void start();

    //Play music
    abstract void sing();

    //The decorations on the music box began to move, the windmill turned and the characters danced
    abstract void action();

    //Turn off the switch
    abstract void end();

    //Let the music box work
    abstract void play();
}

Specific implementation of two kinds of music boxes:

public class Box1 extends MusicBox{
    @Override
    void start() {
        System.out.println("Turn on the start switch of the first music box");
    }

    @Override
    void sing() {
        System.out.println("Play the city of the sky");
    }

    @Override
    void action() {
        System.out.println("The windmill on the music box is turning");
    }

    @Override
    void end() {
        System.out.println("Turn off the start switch of the first music box");
    }

    @Override
    void play() {
        this.start();
        this.sing();
        this.action();
        this.end();
    }
}

public class Box2 extends MusicBox{
    @Override
    void start() {
        System.out.println("Turn on the start switch of the second music box");
    }


    @Override
    void sing() {
        System.out.println("Play the dream wedding");
    }

    @Override
    void action() {
        System.out.println("The couple doll on the music box began to dance");
    }

    @Override
    void end() {
        System.out.println("Turn off the start switch of the second music box");
    }

    @Override
    void play() {
        this.start();
        this.sing();
        this.action();
        this.end();
    }
}

Demonstrate two music boxes to customers:

public class Client {
    public static void main(String[] args) {
        MusicBox box1 = new Box1();
        box1.play();
        System.out.println("===================");
        MusicBox box2 = new Box2();
        box2.play();
    }
}

Operation results:

Oh, it seems that the customer's function has been perfectly realized. But as like as two peas, we can see that the two music boxes are exactly the same in the class. Tut Tut, shouldn't this be mentioned in the abstract parent class? Yeah, come on, do it!

Template mode

public abstract class MusicBox {

    //Turn on the switch
    abstract void start();

    //Play music
    abstract void sing();

    //The decorations on the music box began to move, the windmill turned and the characters danced
    abstract void action();

    //Turn off the switch
    abstract void end();

    //Let the music box work
    public void play(){
        this.start();
        this.sing();
        this.action();
        this.end();
    }
}

public class Box1 extends MusicBox{
    @Override
    void start() {
        System.out.println("Turn on the start switch of the first music box");
    }

    @Override
    void sing() {
        System.out.println("Play the city of the sky");
    }

    @Override
    void action() {
        System.out.println("The windmill on the music box is turning");
    }

    @Override
    void end() {
        System.out.println("Turn off the start switch of the first music box");
    }
}

public class Box2 extends MusicBox{
    @Override
    void start() {
        System.out.println("Turn on the start switch of the second music box");
    }


    @Override
    void sing() {
        System.out.println("Play the dream wedding");
    }

    @Override
    void action() {
        System.out.println("The couple doll on the music box began to dance");
    }

    @Override
    void end() {
        System.out.println("Turn off the start switch of the second music box");
    }
}

Show the customer the same way:

public class Client {
    public static void main(String[] args) {
        MusicBox box1 = new Box1();
        box1.play();
        System.out.println("===================");
        MusicBox box2 = new Box2();
        box2.play();
    }
}

Operation results:

Here is what I said at the beginning: start(),sing(),action(),end(), these are the rules, and play() is the execution order of the rules I specified.
This is the template mode. Is it so easy. The method of using template mode is the same as that of the original mode, and the template mode reduces code redundancy and makes the code more concise.

optimization

  • In the template method, the basic method and method should be designed as protected as far as possible, and other properties and methods should not be set as protected as far as possible
  • In order to prevent malicious operations, the final keyword will be added to the template method version, and overwriting is not allowed
    The so-called basic method is the rule I mentioned above, and the template method is the method I define the rule execution order, so the above code can be written as follows:
public abstract class MusicBox {

    //Turn on the switch
    protected abstract void start();

    //Play music
    protected abstract void sing();

    //The decorations on the music box began to move, the windmill turned and the characters danced
    protected abstract void action();

    //Turn off the switch
    protected abstract void end();

    //Let the music box work
    final public void play(){
        this.start();
        this.sing();
        this.action();
        this.end();
    }
}

public class Box1 extends MusicBox{
    @Override
    protected void start() {
        System.out.println("Turn on the start switch of the first music box");
    }

    @Override
    protected void sing() {
        System.out.println("Play the city of the sky");
    }

    @Override
    protected void action() {
        System.out.println("The windmill on the music box is turning");
    }

    @Override
    protected void end() {
        System.out.println("Turn off the start switch of the first music box");
    }
}

public class Box2 extends MusicBox{
    @Override
    protected void start() {
        System.out.println("Turn on the start switch of the second music box");
    }


    @Override
    protected void sing() {
        System.out.println("Play the dream wedding");
    }

    @Override
    protected void action() {
        System.out.println("The couple doll on the music box began to dance");
    }

    @Override
    protected void end() {
        System.out.println("Turn off the start switch of the second music box");
    }
}

Show the customer the same way:

public class Client {
    public static void main(String[] args) {
        MusicBox box1 = new Box1();
        box1.play();
        System.out.println("===================");
        MusicBox box2 = new Box2();
        box2.play();
    }
}

Operation results:

advantage

  • Encapsulate invariant part
  • Extract the common part code for easy maintenance
  • The behavior is controlled by the parent class and implemented by the child class

shortcoming

  • Contrary to our practice, the results of subclass execution affect the results of the parent class, that is, subclasses affect the parent class
  • If the execution order needs to be changed, modify the code of the abstract class

extend

After reading the two kinds of music boxes demonstrated, the customer put forward a new demand: some of the music boxes we play in sky city have movable decoration, such as windmills and waterwheel, but some have no movable decoration. What's the matter?
Looking at the customer's demand, you are lost in thought. You can't reopen another category for such a small difference. It's too wasteful of resources. There must be some ways to make it compatible

Hook Method

Yes, you can make a music box produce different products according to different decorations by using the hook method in the abstract class. Here, a hook method for judging whether there is movable decoration is added for control,
So we can modify it as follows:

public abstract class MusicBox {

    //Turn on the switch
    protected abstract void start();

    //Play music
    protected abstract void sing();

    //The decoration on the music box began to move, the windmill turned and the characters danced
    protected abstract void action();

    //Turn off the switch
    protected abstract void end();

    //Judge whether there is movable decoration --- hook function. Here, the internal result is changed by external changes
    protected boolean haveActionThings(){
        return true;
    }

    //Let the music box work
    final public void play(){
        this.start();
        this.sing();
        if(this.haveActionThings()) {
            this.action();
        }
        this.end();
    }
}

public class Box1 extends MusicBox{

    private boolean actionThingsTag = true;

    @Override
    protected void start() {
        System.out.println("Turn on the start switch of the first music box");
    }

    @Override
    protected void sing() {
        System.out.println("Play the city of the sky");
    }

    @Override
    protected void action() {
        System.out.println("The windmill on the music box is turning");
    }

    @Override
    protected void end() {
        System.out.println("Turn off the start switch of the first music box");
    }

    @Override
    protected boolean haveActionThings() {
        return actionThingsTag;
    }
    public void setActionThingsTag(boolean actionThingsTag) {
        this.actionThingsTag = actionThingsTag;
    }
}

public class Box2 extends MusicBox{
    @Override
    protected void start() {
        System.out.println("Turn on the start switch of the second music box");
    }


    @Override
    protected void sing() {
        System.out.println("Play the dream wedding");
    }

    @Override
    protected void action() {
        System.out.println("The couple doll on the music box began to dance");
    }

    @Override
    protected void end() {
        System.out.println("Turn off the start switch of the second music box");
    }
}

public class Client {
    public static void main(String[] args) {
        Box1 box1 = new Box1();
        box1.play();
        System.out.println("===================");
        Box1 box11 = new Box1();
        box11.setActionThingsTag(false);
        box11.play();
        System.out.println("===================");
        Box2 box2 = new Box2();
        box2.play();
    }
}

Operation results:

summary

The template method pattern builds a framework through the parent class, and the child class rewrites the parent class method, so that the child class can focus more on the implementation of business code. Important core algorithms can be designed as template methods, and other detailed functions can be implemented in subclasses.

One thousand readers have one thousand Hamlets. The above is purely my personal learning experience. If anything is wrong, please point out that let me learn a wave and correct my mistakes. Thank you!

Topics: Java Design Pattern