1. Command mode concept

1.1 introduction

Command Pattern is a data-driven design Pattern, which belongs to behavior Pattern. The request is wrapped in the object as a command and passed to the calling object. The calling object looks for a suitable object that can handle the command and passes the command to the corresponding object, which executes the command.

1.2 define

Encapsulating a request as an object allows you to parameterize customers with different requests, queue requests, or log requests. And support for undoable operations.

1.3 Application Scenarios
  • The system needs to decouple the request caller and the request receiver so that the caller and receiver do not interact directly.
  • The system needs to specify requests, queue requests (e.g., thread pool + work queue), and execute requests at different times.
  • The system needs to support Undo and Redo operations.
  • The system needs to put together a set of actions that support macro commands.

Command pattern UML class diagram

  • Command (Abstract Command class) : Abstracts Command objects based on different Command types. Write out different implementation classes

  • ConcreteCommand: Implements concrete implementations of abstract command objects

  • Invoker (caller/requester) : The sender of the request, which executes the request through the command object. A caller does not need to determine its receiver at design time, so it is only associated with the abstract command. When the program runs, execute() of the command object is called, indirectly invoking the receiver’s operations.

  • Receiver: The object that receives the action related to the request and actually executes the command. Implement the business processing of the request. The object that actually performs the content of the operation before abstraction.

  • Client: In the Client class, you need to create the caller object, the concrete command object, and specify the corresponding receiver when you create the concrete command object. There is no relationship between sender and receiver, both are invoked through command objects.

3. Command mode code implementation

Tetris game down square, to the right square, left square… Each direction is a command

The Command:
public interface ICommand extends Serializable {
    void execute(a);
}
Copy the code
ConcreteCommand:
public class LeftCommand implements ICommand {
    private Receiver receiver;
    public LeftCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    @Override
    public void execute(a) {
        /** * Do something before execution * such as archive */
        this.receiver.onLeft(); }}public class RightCommand implements ICommand {
    private Receiver receiver;
    public RightCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    @Override
    public void execute(a) {
        this.receiver.onRight(); }}public class BottomCommand implements ICommand {
    private Receiver receiver;
    public BottomCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    @Override
    public void execute(a) {
        this.receiver.onBottom(); }}public class TransfromCommand implements ICommand {
    private Receiver receiver;

    public TransfromCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute(a) {
        this.receiver.onTransformation(); }}Copy the code
Invoker:
public class Invoker { private ICommand leftCommand; private ICommand rightCommand; private ICommand bottomCommand; private ICommand transfromCommand; private List<ICommand> commandList = new ArrayList<>(); public Invoker() { } public Invoker(ICommand leftCommand, ICommand rightCommand, ICommand bottomCommand, ICommand transfromCommand) { this.leftCommand = leftCommand; this.rightCommand = rightCommand; this.bottomCommand = bottomCommand; this.transfromCommand = transfromCommand; } public void toLeft() { this.leftCommand.execute(); commandList.add(leftCommand); } public void toRight() { this.rightCommand.execute(); commandList.add(rightCommand); } public void toBottom() { this.bottomCommand.execute(); commandList.add(bottomCommand); } public void toTransfrom() { this.transfromCommand.execute(); commandList.add(transfromCommand); Public void fallback() {if (commandList.size() > 0) {commandlist.remove (commandlist.size () -1);} /** * fallback */ public void fallback() {if (commandlist.size () > 0) {commandlist.remove (commandlist.size () -1); }} /** * public void saveArchive() {Utils. Serializable ("gameOperation", commandList); } /** * loadArchive */ public void loadArchive() {List<ICommand> List = Utils. Deserialize ("gameOperation"); this.commandList.clear(); this.commandList.addAll(list); for (ICommand command : list) { command.execute(); }}}Copy the code
Receiver:
public class Receiver implements Serializable {

    public void onLeft(a) {
        System.out.println("The left");
    }

    public void onRight(a) {
        System.out.println("Right");
    }

    public void onBottom(a) {
        System.out.println("Down");
    }

    public void onTransformation(a) {
        System.out.println("Deformation"); }}Copy the code
Archive and file reading tools:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;

public class Utils {
    /** * serialize the order object */
    public static void serializable(String name, List
       
         commandList)
        {
        // Serialize the stream of objects
        ObjectOutputStream outputStream = null;
        try {
            outputStream = new ObjectOutputStream(new FileOutputStream(
                    new File("C:\\Users\\Administrator\\Desktop\\" + name + ".txt")));
            outputStream.writeObject(commandList);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(outputStream ! =null) {
                try {
                    outputStream.close();
                } catch(IOException e) { e.printStackTrace(); }}}}public static List<ICommand> deserialize(String name) {
        ObjectInputStream objectInputStream = null;
        try {
            objectInputStream = new ObjectInputStream(new FileInputStream(
                    new File("C:\\Users\\Administrator\\Desktop\\"
                            + name + ".txt")));
            Object readObject = objectInputStream.readObject();
            return (List<ICommand>) readObject;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null; }}Copy the code
Client:
public class Client {

    / * * *@param args
     */
    public static void main(String[] args) {
        /** * receiver */
        Receiver receiver = new Receiver();

        // Command object
        ICommand leftCommand = new LeftCommand(receiver);
        ICommand rightCommand = new RightCommand(receiver);
        ICommand bottomCommand = new BottomCommand(receiver);
        ICommand transfromCommand = new TransfromCommand(receiver);

        / / the requester
        Invoker invoker = new Invoker(leftCommand, rightCommand, bottomCommand, transfromCommand);
        invoker.toLeft();
        invoker.toRight();
        invoker.toBottom();
        invoker.toTransfrom();

        // Serialize the archive
        System.out.println("-- archive -");
        invoker.saveArchive();

        invoker.toBottom();

        System.out.println("- read file -");
        / / read fileinvoker.loadArchive(); }}Copy the code

Result output:

Left to right down deformation ---- archive ---- down ---- read file ---- Left to right down deformationCopy the code

4, command mode used in Android

IHttpCommand is equivalent to Command interface:
/** * Created by Xionghu on 2017/7/4. * Desc: */

public interface IHttpCommand<T extends IRequestParam> {
    public enum RequestType {
        Default(0), Get(1), Post(2), Delete(3);
        private int type;

        private RequestType(int type) {
            this.type = type;
        }

        public int getType(a) {
            returntype; }}public String execute(Context context, String url, RequestType requestType, T requestParam);
}

Copy the code
SystemHttpCommand is equivalent to ConcreteCommand:.
OKHttpCommand omit
public class SystemHttpCommand extends AbsHttpCommand<SystemRequestParam> {
    @Override
    public String executePost(Context context, String url, SystemRequestParam requestParam) {
        // Send the request
        try {
            return HttpUtils.post(url, requestParam.getRequestParam());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String executeGet(Context context, String url, SystemRequestParam requestParam) {
        try {
            return HttpUtils.get(url,requestParam.getRequestParam());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null; }}Copy the code
HttpUtils is equivalent to Receiver:
public class HttpUtils {

    public static String get(String urlStr, Map<String, Object> paramMap)
            throws Exception {
        // Concatenate parameters
        StringBuilder params = new StringBuilder(urlStr + "?");
        int i = 0;
        for (String key : paramMap.keySet()) {
            Object value = paramMap.get(key);
            params.append(key);
            params.append("=");
            params.append(value);
            if (i < paramMap.size() - 1) {
                params.append("&");
            }
            i++;
        }
        return get(params.toString());
    }

    public static String get(String urlStr) {
        String result = null;
        try {
            URL url = new URL(urlStr);
            HttpURLConnection connection = (HttpURLConnection) url
                    .openConnection();
            connection.setReadTimeout(5000);
            connection.setRequestMethod("GET");
            connection.setDoInput(true);
            if (connection.getResponseCode() == 200) {
                InputStream inStream = connection.getInputStream();
                result = new String(StreamTool.readInputStream(inStream));
                returnresult; }}catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public static String post(String urlStr, String username, String password)
            throws Exception {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("username", username);
        paramMap.put("password", password);
        return post(urlStr, paramMap);
    }

    public static String post(String urlStr, Map<String, Object> paramMap)
            throws Exception {
        StringBuffer sb = null;
        // Concatenate parameters
        StringBuilder params = new StringBuilder();
        int i = 0;
        for (String key : paramMap.keySet()) {
            Object value = paramMap.get(key);
            params.append(key);
            params.append("=");
            params.append(value);
            if (i < paramMap.size() - 1) {
                params.append("&");
            }
            i++;
        }
        // Create the request address
        URL url = new URL(urlStr);
        // Open the connection
        HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
        // Set parameters
        httpConn.setDoOutput(true); // Output is required
        httpConn.setDoInput(true); // Input is required
        httpConn.setUseCaches(false); // Cache is not allowed
        httpConn.setRequestMethod("POST"); // Set the POST connection
        // Set the request properties
        httpConn.setRequestProperty("Charset"."UTF-8");
        // Connect, instead of plain connect, use the following httpconn.getOutputStream () to connect automatically
        httpConn.connect();
        // Create an input stream and pass parameters to the pointed URL
        DataOutputStream dos = new DataOutputStream(httpConn.getOutputStream());
        dos.writeBytes(params.toString());
        dos.flush();
        dos.close();
        // Get the response status
        int resultCode = httpConn.getResponseCode();
        sb = new StringBuffer();
        if (HttpURLConnection.HTTP_OK == resultCode) {
            // Parse the data returned by the server
            String readLine = new String();
            BufferedReader responseReader = new BufferedReader(
                    new InputStreamReader(httpConn.getInputStream(), "UTF-8"));
            while((readLine = responseReader.readLine()) ! =null) {
                sb.append(readLine).append("\n");
            }
            responseReader.close();
            return sb.toString();
        }
        return null;
    }

    public interface OnHttpResultListener {
        public void onResult(String result); }}Copy the code
HttpTask is the requester Invoker
public class HttpTask extends AsyncTask<String.Void.String> {

    private HttpTask.Builder.Params p;

    protected HttpTask(HttpTask.Builder.Params p) {
        this.p = p;
    }

    @SuppressWarnings("unchecked")
    @Override
    protected String doInBackground(String... params) {
        try {
            // Execute the command
            return this.p.httpCommand.execute(this.p.context, this.p.url,
                    this.p.requestType, this.p.requestParam);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(String result) {
        if (this.p.onHttpResultListener ! =null) {
            this.p.onHttpResultListener.onResult(result); }}public void builder(a) {
        execute();
    }

    // Use the Builder design mode
    public static class Builder {

        private Params p;

        public Builder(Context context, String url, HttpUtils.OnHttpResultListener onHttpResultListener) {
            this.p = new Params(context, url, onHttpResultListener);
        }

        public Builder setRequestType(IHttpCommand.RequestType requestType) {
            this.p.requestType = requestType;
            return this;
        }

        public Builder setRequestParam(IRequestParam requestParam) {
            this.p.requestParam = requestParam;
            return this;
        }

        public Builder setHttpCommand(IHttpCommand httpCommand) {
            this.p.httpCommand = httpCommand;
            return this;
        }

        public HttpTask build(a) {
            return new HttpTask(p);
        }

        public static class Params {
            public Context context;
            public IHttpCommand.RequestType requestType;
            public String url;
            public IRequestParam requestParam;
            public HttpUtils.OnHttpResultListener onHttpResultListener;
            public IHttpCommand httpCommand;

            public Params(Context context, String url, HttpUtils.OnHttpResultListener onHttpResultListener) {
                this.context = context;
                this.url = url;
                this.requestType = IHttpCommand.RequestType.Get;
                this.httpCommand = new SystemHttpCommand();
                this.requestParam = new SystemRequestParam();
                this.onHttpResultListener = onHttpResultListener; }}}}Copy the code
Client:
        / / the requester
        HttpTask.Builder builder = new HttpTask.Builder(this."".new HttpUtils.OnHttpResultListener() {
            @Override
            public void onResult(String result) {}}); IRequestParam requestParam =new SystemRequestParam();
        requestParam.put(""."");
        builder.setRequestParam(requestParam)
               .setRequestType(IHttpCommand.RequestType.Post)
               .build()
               .builder();

      / / the requester
        HttpTask.Builder builder = new HttpTask.Builder(this."".new HttpUtils.OnHttpResultListener() {
            @Override
            public void onResult(String result) {}}); IRequestParam requestParam =new OKHttpRequestParam();
        requestParam.put(""."");
        builder.setRequestParam(requestParam)
                .setHttpCommand(new OKHttpCommand())
                .setRequestType(IHttpCommand.RequestType.Post).build().builder();

Copy the code

The whole network request architecture adopts command mode, which makes our program more expansible and reduces the coupling (for example, adding setHttpCommand to set okHTTP request is very convenient).

5. Model summary

5.1 the advantages
  • The coupling between requester and implementor is released, and the coupling degree of the system is reduced.

  • Supports undo operations for queuing or logging requests.

  • You can easily design a composite command.

  • New commands can easily be added to the system.

5.2 disadvantages
  • Because a specific command class needs to be designed for each command, using the command pattern can result in a system with too many specific command classes.