an action from the object that actually performs the action Requester == remote control, Object == vendor classes Introduce "command objects" into your design.. Coding Our Command Ob
Trang 1Behavioral design patterns
Trang 3The Command Pattern
Encapsulating Invocation
Trang 4The Command Pattern Example
Trang 5Motivation Example
Program a Remote Control that can be used to
control the devices in the house
Requirements:
Remote control features seven programmable slots
Each slot can be assigned to a different household device
Each slot has a corresponding on/off button
Remote has a global undo button that undoes the last button pressed
A set of vendors classes are provided to give you an idea of the interfaces of the objects that need to be controlled from the remote
Trang 6Global “undo” button
CeilingLight
on ( ) off ( ) dim ( )
TV
on ( ) off ( ) setInputChannel ( ) setVolume ( )
FaucetControl openValue ( ) closeValue ( )
Thermostat setTemperature ( )
Hottub circulate ( ) jetsOn ( ) jetsOff ( ) setTemperature ( ) Vendor Classes
Trang 7Observations, anyone?
methods > common interface
about home automation or how to turn on a hot tub.
Any issues? If the remote is dumb, how do we design a remote so that
it can invoke an action - turn on a light?
future - design must accommodate for that
Trang 8Enter – The Command Pattern!
an action from the object that actually performs the action
Requester == remote control, Object == vendor classes
Introduce "command objects" into your design A command object
encapsulates a request to do something (like turn on a light) on a
specific object (say, the living room light).
Every time a remote button is pressed, its corresponding command
object goes to work!
Remote does not have any idea what the work is, it just has a command object that knows how to talk to the right object to get the work done.
Here the remote is decoupled from the object!
Trang 9Simple Remote Control: Only one button
Trang 10Coding Our Command Object
Command Interface:
}
Implementing a Command to turn the light on
Light light;
this.light = light;
object, which is the light that we are controlling.
Trang 11Using the Command Object
We have one slot to hold our command, which will control one device.
Method for setting the command the slot it’s going to control This could be called if the client wanted to change the behavior of the remote button.
This method is called when the button is pressed All we do is take the current command bound to the slot and call its execute() method.
Trang 12SimpleRemoteControl remote = new
SimpleRemoteControl();
Light light = new Light();
LightOnCommand lightOn = new LightOnCommand(light);
create a Light Object - the Receiver of the request.
create a command and pass it to the Receiver.
pass the command to the Invoker.
simulate the button being pressed.
Trang 13invoking the corresponding operation(s) on Receiver
Knows how to perform the
operations associated with
carrying out a request Any
class may serve as a Receiver
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.
Trang 14Applicability
Use the Command pattern when you want to
function.
The Command's execute() operation can store state for reversing
its effects in the command itself
The Command interface must have an added unexecute()
operation that reverses the effects of a previous call to execute()
case of a system crash
By augmenting the Command interface with load() and store()
operations, you can keep a persistent log of changes
Recovering from a crash involves reloading logged commands from
disk and reexecuting them with the execute() operation
Trang 15The Remote Control Example
Trang 16onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new NoCommand();
for (int i = 0; i < 7; i++) {
Implementing the RemoteControl
This time around the remote is going to handle seven On and Off Commands which we will hold in the corresponding array.
In the constructor all we need to do
is instantiate the on and off arrays.
The setCommand() method takes a slot position and an
On and Off command to be stored in that slot It puts these commands in the On and Off array for later use.
When an On or a Off button is pressed, the hardware takes care of calling the
corresponding methods
Trang 17public StereoOnWithCDCommand (Stereo stereo) {
this.stereo = stereo;
Implementing the Commands
The LightOffCommand works exactly like the
LightOnCommand, except that we are binding
the receiver to a different action: the off()
command.
An instance of the stereo we are going
to control is passed
to constructor
To carry out this request we need to call three methods on the stereo:
first, turn it on, then set it to play the
CD, and finally set the volume to 11 !
Trang 18public class RemoteLoader {
public static void main(String[] args) {
RemoteControl remoteControl = new RemoteControl();
Light livingRoomLight = new Light( "Living Room" );
Create all the Command objects Load the command objects
Ready to roll! Test out the remote!
Trang 19command was loaded for every slot
To get around it, we introduce a "NoCommand" object
Fixing the RemoteControl constructor:
public void onButtonWasPushed(int slot) {
if (onCommands[slot] != null ){
onCommands[slot].execute(); }
}
public class NoCommand implements Command {
public void execute() { };
}
Command noCommand = new NoCommand();
for (int i =0; i < 7; i++ ){
Trang 20public class LightOnCommand implements Command {
public LightOnCommand (Light light){
this.light = light;
method that mirrors the execute() method Whatever execute() last did, undo() reverses it
Adding the Undo Feature
execute() turns the light on so undo turns it off Reverse for LightOffCommand.
public interface Command {
public void execute();
public void undo();
}
Trang 21onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new NoCommand();
for (int i = 0; i < 7; i++) {
Updating the RemoteControl class
This is where we will stash the last command executed for the undo button
Just like the other slots this is initialized
to a NoCommand.
To add support for the undo button
Trang 22When the button is pressed
we take the command and first execute it; then we save
a reference to it in the undoCommand instance variable We do this for both
“on” and “off” commands.
When the undo button is pressed, we invoke the undo() method of the command stored in the
undoCommand This reverses the operation that was last executed.
Trang 23public class CeilingFan {
public static final int HIGH = 3;
public static final int MEDIUM = 2;
public static final int LOW = 1;
public static final int OFF = 0;
String location;
int speed;
public CeilingFan(String location) {
this.location = location;
speed = OFF; }
public void high() {
speed = HIGH;
System.out.println(location + " ceiling fan is on high"); }
public void medium() {
speed = MEDIUM;
System.out.println(location + " ceiling fan is on medium"); }
public void low() {
speed = LOW;
System.out.println(location + " ceiling fan is on low"); }
public void off() {
speed = OFF;
System.out.println(location + " ceiling fan is off"); }
public int getSpeed() {
return speed; }
}
Using State to Implement Undo
Holds local state
Set the speed of the ceiling fan.
Can get the current speed.
Trang 24public CeilingFanHighCommand(CeilingFan ceilingFan) {
this.ceilingFan = ceilingFan;
Adding Undo to Ceiling Fan Commands
Added local state to keep track of the previous speed of the fan.
In execute(), before we change the speed we need to first record its previous state, just in case we need to undo our actions.
To undo, we set the speed of the fan back
to its previous state.
Trang 25Command[] commands;
this.commands = commands;
}
public void execute(){
for (int i = 0; i < commands.length; i++){
MacroCommand
When the macro gets executed by the remote, execute those commands one
at a time.
Executing multiple commands with one
Push one button to dim the lights, turn on the stereo and the
TV and set the to DVD
Trang 26Macro Command
Composite of commands
Trang 27More Uses - Queuing Requests
computation and pass it around as a first-class object.
Add commands to the queue on one end, and on the other end sits a group of threads
Threads run the following script:
Remove command from the queue
Call its execute method
Wait for the call to finish
Discard the command object and retrieve the new one.
that are doing the computation.
Trang 28More Uses: Logging Requests
Semantics of some applications require that we log all actions and be able to recover after a crash by re-invoking those actions
The Command Pattern can store these semantics with the addition of two new methods: store() and load()
In Java we could use serialization to implement this
How does it work?
As execute commands, we store a history of them on disk
When a crash occurs, we reload the command objects and invoke their execute() methods in batch and in order
Trang 29Summary
The Command pattern decouples an object making a request from one that knows how to perform it.
A Command object is at the center of this decoupling and encapsulates
a receiver with an action (or set of actions)
An invoker makes a request of a Command object by calling its
execute () method, which invokes those actions on the receiver.
Invokers can be parameterized with the Commands, even dynamically
at runtime.
Commands may support undo by implementing an undo method that restores the object to its previous state before the execute ( ) method was last called.
Macro commands are a simple extension of Command that allow
multiple commands to be invoked.
Commands may also be used to implement logging and transactional systems.
Trang 30Observer Pattern
Keeping your Objects in the Know!
Trang 31Motivation Weather Station
Weather Station – hardware device which collects data from various sensors (humidity, temperature, pressure)
WeatherData object which interfaces with the Weather Station
hardware
WeatherData and must be updated each time
WeatherData has new measurements
a current conditions display Temp, Humidity, Pressure change
a weather statistics display Avg temp, Min temp, Max temp
and a forecast display
can create new custom display elements and can add or remove as many display elements as they want to the application
Trang 32Pulls data Displays
Current conditions is one of three different displays The user can also get weather stats and a forecast.
Display Devices
Current Conditions
Temp: 72 Humidity: 60 Pressure:
Current Conditions
Temp: 72 Humidity: 60 Pressure:
Weather Data Object Statistics
Avg Temp: 70 Avg Humidity: 63 Avg Pressure:
Statistics
Avg Temp: 70 Avg Humidity: 63 Avg Pressure:
Trang 33We don’t care HOW these variables are set; the WeatherData object knows how to get updated information from the Weather Station
This method is called anytime new weather measurement data
is available
Trang 34// instance variable declarations
currentConditionsDisplay.update(
temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
methods (already implemented)
Now update the displays.
Call each display element to update its display, passing it the most recent measurements
Trang 35public class WeatherData {
// instance variable declarations
public void measurementsChanged() {
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionsDisplay.update(temp, humidity, pressure); statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
Area of change, we need
to encapsulate this.
By coding to concrete implementations
we have no way to add or remove other
display elements without making changes
to the program.
Trang 36The Observer Pattern
OO Design Principle
Strive for loosely coupled designs
between objects that interact.
Loosely coupled designs allow us to build flexible OO
systems that can handle changes because they minimize the interdependency between objects
The Observer Pattern (Intent)
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are
notified and updated automatically
Trang 38Structure Observer Pattern
Knows its numerous observers Provides an interface for
attaching and detaching observer objects
Sends a notification to its observers when its state changes.
Defines an Update interface for concrete observers, that gets called when the
Subject’s state changes.
Concrete observers can be any class that implements the Observer interface Each observer registers with a concrete subject to receive updates.
A concrete subject always implements the Subject interface
In addition to the register (attach) and remove (detach)
methods, the concrete subject implements a notify() method
to notify observers whenever state changes.
Trang 40}
public void update(float temp, float humidity,
float pressure);
}
}
Implementing the Weather Station
These are the state values the Observers get from the Subject when a weather measurement changes.
Trang 41public class WeatherData implements Subject {
private ArrayList observers;
private float temperature ;
private float humidity ;
private float pressure ;
public void notifyObservers() {
for (int j = 0; j < observers.size(); j++) {
Observer observer = (Observer) observers.get(j);
observer.update( temperature , humidity , pressure );
Implementing the Subject Interface
Added an ArrayList to hold the Observers, and we create it in the constructor
Notify the observers when measurements change
Here we implement the Subject Interface
Trang 42implements Observer, DisplayElement {
weatherData.registerObserver(this);
}
float pressure) {
this humidity = humidity;
display();
}
System.out.println( "Current conditions : "
+ temperature + "F degrees and "
+ humidity + "% humidity" );
}
}
The Display Elements
The constructors passed the weatherData object (the subject) and we use it to register the
display as an observer.
When update ( ) is called,
we save the temp and humidity and call display ( )
Trang 43Using Java’s Built-in Observer Pattern
For an Object to become an Observer:
Implement the java.util.Observer interface and call addObserver() on any Observable object To remove use deleteObserver() method.
For the Observable to send notifications
Extend java.util.Observable superclass
Then a 2 step process:
1. First call the setChanged() method to signify that the state has
changed in your object.
2. call one of two methods: notifyObservers() or
notifyObservers(Object arg)
For the Observer to receive notifications
Implement the update() method as before
update(Observable o, Object arg)