Command

August 31, 2014
Author:Eric
Source:http://blog.wjin.org/posts/command.html
Declaration: this work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License. Creative Commons License

Command

Introduction

The command pattern encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations.

Key Point

Example

Considering I have a master server process to dispatch requests (add log, read database, and so on) to worker threads/processes. I can encapsulate request into an object in a work queue, each worker (thread/process) gets task from this queue, worker doesn’t care about which task it gets, it just executes command encapsulated in it.

Following demo code shows using command pattern to implement work queue. Server (command invoker) and worker (command receiver) are decoupled.

For simplicity, server and worker are just wrappered in a class and use global variable to communicate with each other. In real world, it might be multi-thread or multi-process model.

Code

Cpp

// abstract command
class Command
{
public:
    virtual void execute() = 0;
};

// append log command
class AddLog : public Command
{
public:
    void execute()
    {
        cout << "Open file and append log" << endl;
    }
};

// access database command
class AccessDatabase : public Command
{
public:
    void execute()
    {
        cout << "Open database and read tables" << endl;
    }
};

// macro command
// include a few commands together
class AccessAndLog : public Command
{
private:
    vector<shared_ptr<Command>> cmds;

public:
    AccessAndLog(vector<shared_ptr<Command>> &v) : cmds(v) {}
    void execute()
    {
        for (auto e : cmds)
            e->execute();
    }
};

// shared queue between server and worker
queue<shared_ptr<Command>> workqueue;

// command invoker
class Server
{
public:
    void addCommand(shared_ptr<Command> cmd)
    {
        workqueue.push(cmd);
    }
};

// command receiver
class Worker
{
public:
    void excuteCommand()
    {
        while (!workqueue.empty()) {
            shared_ptr<Command> cmd = workqueue.front();
            workqueue.pop();
            cmd->execute();
        }
    }
};

int main(int argc, const char* argv[])
{
    Server s;
    Worker w;

    shared_ptr<Command> log(new AddLog());
    shared_ptr<Command> db(new AccessDatabase());
    vector<shared_ptr<Command>> v = { log, db };
    shared_ptr<Command> macro(new AccessAndLog(v));

    s.addCommand(log);
    s.addCommand(db);
    s.addCommand(macro);

    w.excuteCommand();

    return 0;
}

Java

import java.util.LinkedList;
import java.util.Queue;

// abstract command
interface Command {
	public void execute();
};

// append log command
class AddLog implements Command {
	public void execute() {
		System.out.println("Open file and append log");
	}
};

// access database command
class AccessDatabase implements Command {
	public void execute() {
		System.out.println("Open database and read tables");
	}
};

// macro command
// include a few commands together
class AccessAndLog implements Command {
	private final Command[] cmds;

	public AccessAndLog(Command[] v) {
		cmds = v;
	}

	public void execute() {
		for (Command e : cmds)
			e.execute();
	}
};

// shared queue between Server and Worker
class SharedQueue {
	public static final Queue<Command> workqueue = new LinkedList<Command>();
}

// command invoker
class Server {
	public void addCommand(Command cmd) {
		SharedQueue.workqueue.add(cmd);
	}
}

// command receiver
class Worker {
	public void excuteCommand() {
		while (!SharedQueue.workqueue.isEmpty()) {
			Command c = SharedQueue.workqueue.remove();
			c.execute();
		}
	}
}

public class CommandTest {
	public static void main(String[] args) {
		Server s = new Server();
		Worker w = new Worker();

		Command log = new AddLog();
		Command db = new AccessDatabase();
		Command[] v = { log, db };
		Command macro = new AccessAndLog(v);

		s.addCommand(log);
		s.addCommand(db);
		s.addCommand(macro);

		w.excuteCommand();
	}
}