4
\$\begingroup\$

Below is the code I have written to enhance my understanding of Command Pattern in Java. As per my study and understanding, when we have some entities [ which we call as receivers ] then Command pattern is useful in encapsulating those unrelated receivers and the operations in command objects. Please provide me feedback on my below implementation of command pattern [more on pattern usage perspective].

1. AirConditioner.java

package learn.java.commandpattern.recievers;

/**
 * @author krishna.k
 *
 */
public class AirConditioner {
    public enum Mode {
            HEAT, COOL
    }

    private Mode acMode;
    private int temperature = 16;

    /**
     * @param newTemp
     * @return
     */
    public boolean setTempreature(int newTemp) {
            boolean isSuccess = false;
            if (newTemp <= 30 && newTemp >= 16) {
                    temperature = newTemp;
                    System.out.println("Temperature has been successfully set as " + temperature);
                    isSuccess = true;
            }
            return isSuccess;
    }

    /**
     * @param newACMode
     */
    public void setACMode(Mode newACMode) {
            this.acMode = newACMode;
            System.out.println("AC mode has been set as " + acMode);
    }

    public void switchOnAC() {
            System.out.println("AC has been switched ON");
    }

    public void switchOffAC() {
            System.out.println("AC has been switched OFF");
    }

    public int getTemp() {
            return temperature;
    }

    public Mode getACMode() {
            return acMode;
    }
}

2. Bulb.java

package learn.java.commandpattern.recievers;

/**
 * @author krishna.k
 *
 */
public class Bulb {
    public void setON() {
            System.out.println("Bulb has been switched ON");
    }

    public void setOFF() {
            System.out.println("Bulb has been switched OFF");
    }
}

3. Fan.java

package learn.java.commandpattern.recievers;

/**
 * @author krishna.k
 *
 */
public class Fan {
    /**
     * 
     * This enum corresponds to the allowed values for the speed
     *
     */
    public enum Speed {
            ZERO, ONE, TWO, THREE, FOUR, FIVE
    }

    private Speed speed = Speed.ZERO;

    public void switchOn() {
            System.out.println("Fan has been switched on");
    }

    public void switchOff() {
            System.out.println("Fan has been switched off");
    }

    /**
     * @param newSpeed
     */
    public void changeSpeed(Speed newSpeed) {
            speed = newSpeed;
            System.out.println("Speed of the fan has been set as " + speed);
    }

    /**
     * @return Speed of the fan object
     */
    public Speed getSpeed() {
            return speed;
    }
}

4. Microwave.java

package learn.java.commandpattern.recievers;

/**
 * @author krishna.k
 *
*/
public class Microwave {
    /**
     * This enum corresponds to the allowed Power for the microwave object
     *
     */
    public enum Power {
            W700, W800, W900, W1000
    }

    private Power power = Power.W700;

    public void switchOnMW() {
            System.out.println("Microwave has been switched ON");
    }

    public void switchOffMW() {
            System.out.println("Microwave has been switched OFF");
    }

    /**
     * @param watt
     */
    public void setPower(Power watt) {
            power = watt;
            System.out.println("Power has been set as " + power);
    }

    /**
     * @return Power for the current Microwave object
     */
    public Power getPower() {
            return power;
    }
}

Below are the Command interface and the Concrete Command Classes

Command Interface

package learn.java.commandpattern.commands;

/**
 * @author krishna.k
 * 
 *         This is the interface which is implemented by the concrete Command
 *         Classes.
 *
 */
public interface Command {
    /**
     * Executed the operation
     */
    public void execute();

    /**
     * reverses the operation performed in the execute method.
     */
    public void undo();
}

1. ACModeCommand.java

package learn.java.commandpattern.commands;

import learn.java.commandpattern.recievers.AirConditioner;
import learn.java.commandpattern.recievers.AirConditioner.Mode;

/**
 * 
 * @author krishna.k
 * 
 *         This is Command for handling the AC mode. This encapsulates the
 *         receiver AC object and the operation to perform n it i.e mode setting
 *
 */
public class ACModeCommand implements Command {
    private Mode newACMode;
    private AirConditioner recieverAC;
    private Mode previousModeAC;

    /**
     * This enum is for the clients, in case of some complex code change in the
     * receiver code done later, that can be handled here. This enum shall make
     * the client less dependent on the receiver.
     *
     */
    public enum ACMode {
            HEAT, COOL
    }

    /**
     * @param recieverAC
     * @param modeAC
     */
    public ACModeCommand(AirConditioner recieverAC, ACMode modeAC) {
            this.newACMode = getACMode(modeAC);
            this.recieverAC = recieverAC;
    }

    /**
     * @param acMode
     * @return
     */
    private Mode getACMode(ACMode acMode) {
            return (acMode == ACMode.COOL) ? Mode.COOL : Mode.HEAT;
    }

    /*
     * (non-Javadoc)
     * 
     * @see learn.java.commandpattern.commands.Command#execute()
     */
    @Override
    public void execute() {
            previousModeAC = recieverAC.getACMode();
            recieverAC.setACMode(newACMode);
    }

    /*
     * (non-Javadoc)
     * 
     * @see learn.java.commandpattern.commands.Command#undo()
     */
    @Override
    public void undo() {
            recieverAC.setACMode(previousModeAC);
    }

}

2. ACTempreatureCommand.java

package learn.java.commandpattern.commands;

import learn.java.commandpattern.recievers.AirConditioner;

/**
 * @author krishna.k
 * 
 *         This is the Command class encapsulating the AC object reciever and
 *         the operation on it i.e to set the tempreature
 *
 */
public class ACTempreatureCommand implements Command {
    private AirConditioner recieverAC;
    private int previousTemperature;
    private int newTemperature;
    private boolean isExecuteSucess = false;

    /**
     * @param recieverAC
     * @param newTemperature
     */
    public ACTempreatureCommand(AirConditioner recieverAC, int newTemperature) {
            this.recieverAC = recieverAC;
            this.newTemperature = newTemperature;
    }

    /*
     * (non-Javadoc)
     * 
     * @see learn.java.commandpattern.commands.Command#execute()
     */
    @Override
    public void execute() {
            previousTemperature = recieverAC.getTemp();
            isExecuteSucess = recieverAC.setTempreature(newTemperature);
    }

    /*
     * (non-Javadoc)
     * 
     * @see learn.java.commandpattern.commands.Command#undo()
     */
    @Override
    public void undo() {
            // undo only if the execute performed the task i.e. temperature was set
            // as per the client provided temperature.
            if (isExecuteSucess) {
                    recieverAC.setTempreature(previousTemperature);
            }
    }
}

3. FANSpeedCommand.java

package learn.java.commandpattern.commands;

import learn.java.commandpattern.recievers.Fan;
import learn.java.commandpattern.recievers.Fan.Speed;

/**
 * @author krishna.k
 *
 */
public class FANSpeedCommand implements Command {
    private Fan recieverFan;
    private Speed previousSpeed;
    private Speed newSpeed;

    /**
     * This enum prevents the client from being too much coupled with the
     * receiver code. Client code can control the receivers using Command
     * objects.
     *
     */
    public enum FANSpeed {
            ZERO, ONE, TWO, THREE, FOUR, FIVE
    }

    /**
     * @param recieverFan
     * @param newSpeed
     */
    public FANSpeedCommand(Fan recieverFan, FANSpeed newSpeed) {
            this.recieverFan = recieverFan;
            this.newSpeed = getFanSpeed(newSpeed);
    }

    /**
     * @param speed
     * @return
     */
    private Speed getFanSpeed(FANSpeed speed) {
            Speed fanSpeed = Speed.ZERO;
            switch (speed) {
            case FIVE:
                    fanSpeed = Speed.FIVE;
                    break;
            case FOUR:
                    fanSpeed = Speed.FOUR;
                    break;
            case ONE:
                    fanSpeed = Speed.ONE;
                    break;
            case THREE:
                    fanSpeed = Speed.THREE;
                    break;
            case TWO:
                    fanSpeed = Speed.TWO;
                    break;
            case ZERO:
                    fanSpeed = Speed.ZERO;
                    break;
            }
            return fanSpeed;
    }

    /*
     * (non-Javadoc)
     * 
     * @see learn.java.commandpattern.commands.Command#execute()
     */
    @Override
    public void execute() {
            previousSpeed = recieverFan.getSpeed();
            recieverFan.changeSpeed(newSpeed);
    }

    /*
     * (non-Javadoc)
     * 
     * @see learn.java.commandpattern.commands.Command#undo()
     */
    @Override
    public void undo() {
            recieverFan.changeSpeed(previousSpeed);
    }
}

4. MWPowerCommand.java

package learn.java.commandpattern.commands;

import learn.java.commandpattern.recievers.Microwave;
import learn.java.commandpattern.recievers.Microwave.Power;

/**
 * @author krishna.k
 * 
 *         This is concrete command object encapsulating the Receiver Microwave
 *         object and the operation to perform on it i.e to set its Power
 *
 */
public class MWPowerCommand implements Command {
    /**
     * This enum prevents the client from being too much coupled with the
     * receiver code. Client code can control the receivers using Command
     * objects.
     *
     */
    public enum MWPower {
            W700, W800, W900, W1000
    }

    private Power previousMWPower;
    private Power newMWPower;
    private Microwave recieverMW;

    /**
     * @param recieverMW
     * @param newPower
     */
    public MWPowerCommand(Microwave recieverMW, MWPower newPower) {
            this.recieverMW = recieverMW;
            this.newMWPower = getMWpower(newPower);
    }

    /*
     * (non-Javadoc)
     * 
     * @see learn.java.commandpattern.commands.Command#execute()
     */
    @Override
    public void execute() {
            previousMWPower = recieverMW.getPower();
            recieverMW.setPower(newMWPower);
    }

    /*
     * (non-Javadoc)
     * 
     * @see learn.java.commandpattern.commands.Command#undo()
     */
    @Override
    public void undo() {
            recieverMW.setPower(previousMWPower);
    }

    /**
     * @param power
     * @return
     */
    private Power getMWpower(MWPower power) {
            Power mwPower = Power.W700;
            switch (power) {
            case W1000:
                    mwPower = Power.W1000;
                    break;
            case W700:
                    mwPower = Power.W700;
                    break;
            case W800:
                    mwPower = Power.W800;
                    break;
            case W900:
                    mwPower = Power.W900;
                    break;
            }
            return mwPower;
    }

}

5. OnOffCommand.java

package learn.java.commandpattern.commands;

import learn.java.commandpattern.recievers.AirConditioner;
import learn.java.commandpattern.recievers.Bulb;
import learn.java.commandpattern.recievers.Fan;
import learn.java.commandpattern.recievers.Microwave;

/**
 * @author krishna.k
 * 
 *         This is concrete command object, which enables client code to become
 *         decoupled with the receiver APIs. It provides similar way to switch
 *         on and off an receiver
 *
 */
public class OnOffCommand implements Command {
    /**
     * 
     */
    private Object reciever;
    /**
     * 
     */
    private boolean isToPutOn;

    /**
     * @param reciever
     * @param isToPutOn
     */
    public OnOffCommand(Object reciever, boolean isToPutOn) {
            this.reciever = reciever;
            this.isToPutOn = isToPutOn;
    }

    /*
     * (non-Javadoc)
     * 
     * @see learn.java.commandpattern.commands.Command#execute()
     */
    @Override
    public void execute() {
            if (isToPutOn) {
                    executeOn();
            } else {
                    executeOff();
            }
    }

    /**
     * 
     */
    private void executeOn() {
            if (reciever instanceof Fan) {
                    ((Fan) reciever).switchOn();
            } else if (reciever instanceof AirConditioner) {
                    ((AirConditioner) reciever).switchOnAC();
            } else if (reciever instanceof Bulb) {
                    ((Bulb) reciever).setON();
            } else if (reciever instanceof Microwave) {
                    ((Microwave) reciever).switchOnMW();
            }

    }

    /**
     * 
     */
    private void executeOff() {
            if (reciever instanceof Fan) {
                    ((Fan) reciever).switchOff();
            } else if (reciever instanceof AirConditioner) {
                    ((AirConditioner) reciever).switchOffAC();
            } else if (reciever instanceof Bulb) {
                    ((Bulb) reciever).setOFF();
            } else if (reciever instanceof Microwave) {
                    ((Microwave) reciever).switchOffMW();
            }

    }

    /*
     * (non-Javadoc)
     * 
     * @see learn.java.commandpattern.commands.Command#undo()
     */
    @Override
    public void undo() {
            if (isToPutOn) {
                    executeOff();
            } else {
                    executeOn();
            }
    }

}

Below is the CommandInvoker.java

package learn.java.commandpattern.commands.invoker;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;

import learn.java.commandpattern.commands.Command;

/**
 * @author krishna.k
 * 
 *         This is the invoker class which invokes the execute method on the
 *         command queue.
 *
 */
public class CommandInvoker {
    private Deque<Command> commandQueue;

    public CommandInvoker() {
            commandQueue = new ArrayDeque<Command>();
    }

    public void addCommand(Command newCommand) {
            commandQueue.addFirst(newCommand);
    }

    /**
     * iterates the command queue and performs execution of the commands as set
     * by the client code.
     */
    public void invokeExecute() {
            Iterator<Command> iterator = commandQueue.descendingIterator();
            while (iterator.hasNext()) {
                    Command cmd = iterator.next();
                    cmd.execute();
            }
    }

    /**
     * Iterates the command queue and performs the undo operation.
     * 
     */
    public void invokeUnDo() {
            Iterator<Command> iterator = commandQueue.iterator();
            while (iterator.hasNext()) {
                    Command cmd = iterator.next();
                    cmd.undo();
            }
    }
}

And Finally the Client.java

package learn.java.commandpattern.client;

import learn.java.commandpattern.commands.ACModeCommand;
import learn.java.commandpattern.commands.ACTempreatureCommand;
import learn.java.commandpattern.commands.FANSpeedCommand;
import learn.java.commandpattern.commands.FANSpeedCommand.FANSpeed;
import learn.java.commandpattern.commands.MWPowerCommand;
import learn.java.commandpattern.commands.OnOffCommand;
import learn.java.commandpattern.commands.invoker.CommandInvoker;
import learn.java.commandpattern.recievers.AirConditioner;
import learn.java.commandpattern.recievers.Bulb;
import learn.java.commandpattern.recievers.Fan;
import learn.java.commandpattern.recievers.Microwave;

/**
 * 
 * @author Krishna.k
 * 
 *         This is the client code. With the use of Command pattern, this code
 *         is very less dependent upon the receiver. By Using Command Pattern it
 *         is also easy to have Undo operations easily.
 */
public class Client {
    public static void main(String[] args) {
            Microwave microWave = new Microwave();
            Fan fan = new Fan();
            Bulb bulb = new Bulb();
            AirConditioner ac = new AirConditioner();
            CommandInvoker cmdInvoker = new CommandInvoker();
            cmdInvoker.addCommand(new OnOffCommand(microWave, true));
            cmdInvoker.addCommand(new OnOffCommand(fan, true));
            cmdInvoker.addCommand(new OnOffCommand(ac, true));
            cmdInvoker.addCommand(new OnOffCommand(bulb, true));
            cmdInvoker.addCommand(new ACModeCommand(ac, ACModeCommand.ACMode.COOL));
            cmdInvoker.addCommand(new ACTempreatureCommand(ac, 20));
            cmdInvoker.addCommand(new FANSpeedCommand(fan, FANSpeed.ONE));
            cmdInvoker.addCommand(new MWPowerCommand(microWave, MWPowerCommand.MWPower.W1000));
            cmdInvoker.addCommand(new FANSpeedCommand(fan, FANSpeed.TWO));
            cmdInvoker.addCommand(new FANSpeedCommand(fan, FANSpeed.THREE));
            cmdInvoker.addCommand(new MWPowerCommand(microWave, MWPowerCommand.MWPower.W700));
            cmdInvoker.addCommand(new FANSpeedCommand(fan, FANSpeed.FOUR));
            cmdInvoker.addCommand(new ACTempreatureCommand(ac, 13));
            cmdInvoker.addCommand(new ACTempreatureCommand(ac, 25));
            cmdInvoker.addCommand(new MWPowerCommand(microWave, MWPowerCommand.MWPower.W1000));
            cmdInvoker.addCommand(new ACTempreatureCommand(ac, 17));
            cmdInvoker.addCommand(new ACTempreatureCommand(ac, 18));
            cmdInvoker.addCommand(new ACTempreatureCommand(ac, 29));
            cmdInvoker.invokeExecute();
            cmdInvoker.invokeUnDo();
    }
}
\$\endgroup\$
2
\$\begingroup\$

Code smell using instanceof

        if (reciever instanceof Fan) {
                ((Fan) reciever).switchOn();
        } else if (reciever instanceof AirConditioner) {
                ((AirConditioner) reciever).switchOnAC();
        } else if (reciever instanceof Bulb) {
                ((Bulb) reciever).setON();
        } else if (reciever instanceof Microwave) {
                ((Microwave) reciever).switchOnMW();
        }

This usage of instanceof is a code smell, what happens when you buy a new fridge, then this class need to learn how to turn off and on the fridge to.

A better approach to this system would making a LinePoweredDevice class/interface, that contains these methods:

public interface LinePoweredDevice {
    public void turnOn();

    public void turnOff();
}

And then implement it in every class, and adjust your on/off command to this new class.

Order of cases in a switch should be easy for humans to read

        switch (speed) {
        case FIVE:
                fanSpeed = Speed.FIVE;
                break;
        case FOUR:
                fanSpeed = Speed.FOUR;
                break;
        case ONE:
                fanSpeed = Speed.ONE;
                break;
        case THREE:
                fanSpeed = Speed.THREE;
                break;
        case TWO:
                fanSpeed = Speed.TWO;
                break;
        case ZERO:
                fanSpeed = Speed.ZERO;
                break;
        }

You have the options in the following order:

  • FIVE
  • FOUR
  • ONE
  • THREE
  • TWO
  • ZERO

When looking at this code, it takes extra long to realize all the constants are there, just because they are in a non-human order.

Inconsistent naming for name of the temperature variable

public boolean setTempreature(int newTemp) {
public int getTemp() {

Either have getTemparature() and setTemparature() or getTemp() and setTemp().

OnOffCommand does not respect already on state of a device

This class does not check if a device is already turned on or off, it may happen that this class turns off a light bulb when undoing, even when this bulb was on before the operation even started.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the review... I will correct the enum state order and naming of the setter and getters. For the review comment for the creating a new interface 'LinePoweredDevice'. The receivers are non related. Consider them as code coming from some different libraries. \$\endgroup\$ – Krishna Kumar Feb 15 '16 at 13:35
2
\$\begingroup\$

You can get rid of instanceOf method.

  1. Create Receiver interface (or abstract class)
  2. Provide concrete implementation of Receiver.

Command workflow should be like this:

Client calls Invoker => Invoker calls ConcreteCommand => ConcreteCommand calls Receiver method, which implements abstract Command method.

Example:

    // On command for TV with same invoker 
    Receiver r = new TV();
    Command onCommand = new OnCommand(r);
    Invoker invoker = new Invoker(onCommand);
    invoker.execute();

    // On command for DVDPlayer with same invoker 
    r = new DVDPlayer();
    onCommand = new OnCommand(r);
    invoker = new Invoker(onCommand);
    invoker.execute();

You can find working code at this SE question:

https://stackoverflow.com/questions/35276941/how-commnd-pattern-decouples-the-sender-from-reciever/35296198?noredirect=1#comment58524415_35296198

\$\endgroup\$
  • \$\begingroup\$ I understand that instead of instanceof an interface for recievers can be made but as per what I have read and understood, in command pattern receivers are already there may be written by some third parties and may be availabe through some differnent libraries... I mean receivers are not related... as per the UML diagram (generated by staruml for command patrern and also in various places in internet) does not has a common base interface for receivers... only ICommand is there as base for all concrete commands. \$\endgroup\$ – Krishna Kumar Feb 16 '16 at 10:31
  • \$\begingroup\$ Like Serializable marker interface, which does not have any relation between implementing classes, just create IReceiver. It will help to get rid of instanceof call. \$\endgroup\$ – Ravindra babu Feb 16 '16 at 10:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.