ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Command
    Modeling/DesignPattern 2020. 2. 29. 16:29

    1. Overview

    • We want to represent a request or a method call as an object. Information about parameters passed and the actual operation is encapsulated in an object called command.
    • The advantage of a command pattern is that what would have been a method call is now an object which can be stored for later execution or sent to other parts of code.
    • We can now even queue our command objects and execute them later.

    2. Description

    2.1 Implementation

    • Command interface must define a method which executes the command
    • Implement this interface in class for each request or operation type we want to implement. Command should also allow for undo operation if your system needs it.
    • Each concrete command knows exactly which operation it needs. All it needs is parameters for the operation if required and the receiver instance on which operation is invoked.
    • Client creates the command instance and sets up the receiver and all required parameters.
    • The command instance is then ready to be sent to other parts of the code. Invoker is the code that actually uses command instance and invokes the execution on the command.

    2.2  Considerations

    2.2.1 Implementation

    • You can support undo and redo in your commands. This makes them really useful for systems with complex user interactions like workflow designer.
    • If your command is simple i.e. if it doesn't have to undo feature, doesn't have any state and simply hides a particular function And its arguments then you can reuse same command object for the same type of request
    • For commands that are going to be queued for long durations, pay attention to size of state maintained by them

    2.2.2 Design

    • Commands can be inherited from other commands to reuse portions of code and build the base
    • You can also compose commands with other commands as well. These macro commands will have one or more sub-commands executed in sequence to complete a request
    • For implementing undo feature in your command you can make use of memento design pattern, which allows commands to store the state information of receiver without knowing about internal objects used by the receiver

    2.3 Pitfalls

    • Things get a bit controversial when it comes to returning values and error handling with command
    • Error handling is difficult to implement without coupling the command with the client. In cases where a client needs to know a return value of execution, it's the same situation.
    • In code where invoker is running in a different thread, which is very common in situations where command pattern is used, error handling and returns values get lot more complicated to handle

    3. Usage

    3.1 java.lang.Runnable

    3.2 The Action class in struts framework

    4. Comparison with Strategy

    Command Strategy
    Command contains which operation is being executed by the receiver Strategy actually contains how the operation is to be carried out
    Command encapsulates an actions Strategy encapsulates a particular algorithm

    5. Example

    //Interface implemented by all concrete
    //command classes
    interface Command {
        void execute();
    }
    //A Concrete implementation of Command.
    class AddMemberCommand implements Command {
        private String emailAddress;
        private String listName;
        private EWSService receiver;
        public AddMemberCommand(String email, String listName, EWSService service) {
    
            this.emailAddress = email;
            this.listName = listName;
            this.receiver = service;
        }
        @Override
        public void execute() {
            receiver.addMember(emailAddress, listName);
        }
    }
    //This class is the receiver.
    class EWSService {
        //Add a new member to mailing list
        public void addMember(String contact, String contactGroup) {
            //contact exchange
            System.out.println("Added "+contact +" to "+contactGroup);
        }
        //Remove member from mailing list
        public void removeMember(String contact, String contactGroup) {
            //contact exchange
            System.out.println("Removed "+contact +" from "+contactGroup);
        }
    }
    //Throw Away POC code DON'T USE in PROD
    //This is invoker actually executing commands.
    //starts a worker thread in charge of executing commands
    // MailTasksRunner itself is singleton
    class MailTasksRunner implements Runnable {
    
        private Thread runner;
        private List<Command> pendingCommands;
        private volatile boolean stop;
        private static final MailTasksRunner RUNNER = new MailTasksRunner();
        public static final MailTasksRunner getInstance() {
            return RUNNER;
        }
        private MailTasksRunner() {
            pendingCommands = new LinkedList<>();
            runner = new Thread(this);
            runner.start();
        }
    
        //Run method takes pending commands and executes them.
        @Override
        public void run() {
    
            while (true) {
                Command cmd = null;
                synchronized (pendingCommands) {
                    if (pendingCommands.isEmpty()) {
                        try {
                            pendingCommands.wait();
                        } catch (InterruptedException e) {
                            System.out.println("Runner interrupted");
                            if (stop) {
                                System.out.println("Runner stopping");
                                return;
                            }
                        }
                    } else {
                        cmd = pendingCommands.remove(0);
                    }
                }
                if (cmd == null)
                    return;
                cmd.execute();
            }
    
        }
    
        //Giving it a command will schedule it for later execution
        public void addCommand(Command cmd) {
            synchronized (pendingCommands) {
                pendingCommands.add(cmd);
                pendingCommands.notifyAll();
            }
        }
    
        public void shutdown() {
            this.stop = true;
            this.runner.interrupt();
        }
    }
    public class Client {
        public static void main(String[] args) throws InterruptedException {
            EWSService service = new EWSService();
            Command c1 = new AddMemberCommand("a@a.com", "spam", service);
            MailTasksRunner.getInstance().addCommand(c1);
    
            Command c2 = new AddMemberCommand("b@b.com", "spam", service);
            MailTasksRunner.getInstance().addCommand(c2);
    
            Thread.sleep(3000);
            MailTasksRunner.getInstance().shutdown();
        }
    }
    

    6. Reference

    https://en.wikipedia.org/wiki/Command_pattern

    https://courseprobe.com/instructors/coffee-powered-crew/

    'Modeling > DesignPattern' 카테고리의 다른 글

    Visitor  (0) 2020.02.29
    Iterator  (0) 2020.02.29
    Flyweight  (0) 2020.02.28
    Dependency Inversion Principle (DIP)  (0) 2020.02.28
    Interface Segregation Principle (ISP)  (0) 2020.02.28

    댓글

Designed by Tistory.