Refactoring the player control bar using command mode

Keywords: Java Design Pattern architecture

This article is excerpted from "design patterns should be learned this way"

1 UML class diagram of command pattern

The UML class diagram of the command pattern is shown in the following figure.

2 reconstruct the player control bar using command mode

If we develop a player, the player has playback function, drag progress bar function, stop playback function and pause function. When we operate the player, we do not directly call the method of the player, but convey instructions to the inner core of the player through a control bar. The specific instructions will be encapsulated into buttons. Then each button is equivalent to the encapsulation of a command. The control bar is used to decouple the instructions sent by the user from the instructions received by the player kernel. Let's look at the code. First, create the player kernel GPlayer class.

public class GPlayer {

    public void play(){
        System.out.println("Normal play");
    }

    public void speed(){
        System.out.println("Drag progress bar");
    }

    public void stop(){
        System.out.println("stop playing");
    }

    public void pause(){
        System.out.println("Pause playback");
    }
        
}

Create command interface IAction class.

public interface IAction {

    void execute();
        
}

Then create instructions that can be received by the operation player respectively. The code of PlayAction class of playing instructions is as follows.

public class PlayAction implements IAction {

    private GPlayer gplayer;

    public PlayAction(GPlayer gplayer) {
        this.gplayer = gplayer;
    }

    public void execute() {
        gplayer.play();
    }
        
}

The code of pause instruction PauseAction class is as follows.

public class PauseAction implements IAction {

    private GPlayer gplayer;

    public PauseAction(GPlayer gplayer) {
        this.gplayer = gplayer;
    }

    public void execute() {
        gplayer.pause();
    }
        
}

The code of the SpeedAction class of the drag progress bar instruction is as follows.

public class SpeedAction implements IAction {

    private GPlayer gplayer;

    public SpeedAction(GPlayer gplayer) {
        this.gplayer = gplayer;
    }

    public void execute() {
        gplayer.speed();
    }
        
}

The code of StopAction class of stop playing instruction is as follows.

public class StopAction implements IAction {

    private GPlayer gplayer;

    public StopAction(GPlayer gplayer) {
        this.gplayer = gplayer;
    }

    public void execute() {
        gplayer.stop();
    }
}

Finally, create the control bar Controller class.

public class Controller {
    private List<IAction> actions = new ArrayList<IAction>();
    public void addAction(IAction action){
        actions.add(action);
    }

    public void execute(IAction action){
        action.execute();
    }

    public void executes(){
        for(IAction action : actions){
            action.execute();
        }
        actions.clear();
    }
}

From the above code, the control bar can execute a single command or multiple commands in batch. Let's look at the client test code.

public static void main(String[] args) {

        GPlayer player = new GPlayer();
        Controller controller = new Controller();
        controller.execute(new PlayAction(player));

        controller.addAction(new PauseAction(player));
        controller.addAction(new PlayAction(player));
        controller.addAction(new StopAction(player));
        controller.addAction(new SpeedAction(player));
        controller.executes();
}
        

Since the control bar has been decoupled from the player kernel, if you want to expand new commands in the future, you only need to add commands, and the structure of the control bar does not need to be changed.

Application of 3 Command Mode in JDK source code

First, look at the runnable interface in the JDK. Runnable is equivalent to the abstraction of commands. Any class that implements the runnable interface is considered a thread.

public interface Runnable {
    public abstract void run();
}

In fact, after calling the thread's start() method, you are qualified to grab CPU resources without writing logic to obtain CPU resources. After the thread grabs the CPU resources, it will execute the contents of the run() method, and use the Runnable interface to decouple the user request from the CPU execution.

Application of 4 command mode in JUnit source code

Let's take a look at the junit.framework.Test interface that we are very familiar with.

package junit.framework;

public interface Test {
    public abstract int countTestCases();

    public abstract void run(TestResult result);
}

There are two methods in the Test interface. The first is the countTestCases() method, which is used to count the total number of Test cases to be executed. The second is the run() method, which is used to execute specific Test logic, and its parameter TestResult is used to return Test results. In fact, when we write Test cases at ordinary times, we only need to implement the Test interface, which is considered as a Test case, and it will be automatically recognized during execution. The usual approach is to inherit the TestCase class. You might as well take a look at the source code of TestCase.

public abstract class TestCase extends Assert implements Test {
        ...
        public void run(TestResult result) {
        result.run(this);
     }
     ...
}

In fact, the TestCase class also implements the Test interface. We inherit the TestCase class, which is equivalent to implementing the Test interface. Naturally, it will be scanned into a Test case.

Posted by ghjr on Mon, 22 Nov 2021 15:10:15 -0800