Command
The command pattern
The command pattern is a behavioral pattern and it encapsulates an object allowing undoable or delayed actions.The structure is such that there is always, a Command, Receiver, Client and Invoker, as in the diagram:
It's quite a useful pattern with the undoable or delayed action probably being it's strong point. So to illustrate the pattern I've created a abstraction:
void Execute();
}
List
public Application() {
documents = new List
}
internal void Add(Document document) {
documents.Add(document);
Console.WriteLine($"Adding document with name {document.GetName()} to Application.");
}
internal Document GetActiveDocument() {
return documents[documents.Count-1];
}
}
private string name;
private Document(string name) {
this.name = name;
}
public string GetName() {
return this.name;
}
public static Document GetNewDocument(string newDocumentName) {
Console.WriteLine($"Creating and returning document named {newDocumentName}.");
return new Document(newDocumentName);
}
internal void Paste() {
Console.WriteLine($"Pasting document named {name}.");
}
}
private Application application;
private string newDocumentName;
public OpenCommand(Application application) {
this.application = application;
}
public void Execute() {
promptForNewDocumentName();
application.Add(Document.GetNewDocument(newDocumentName));
}
private void promptForNewDocumentName() {
Console.Write("Document name?> ");
newDocumentName = Console.ReadLine();
}
}
I created another command object using the same command abstraction, which is:
private Document document;
public PasteCommand(Document document) {
this.document = document;
}
public void Execute() {
document.Paste();
}
}
Application application = new Application();
ICommand openCommand = new OpenCommand(application);
ICommand pasteCommand;
openCommand.Execute();
pasteCommand = new PasteCommand(application.GetActiveDocument());
pasteCommand.Execute();
Console.ReadKey();
}
Creating and returning document named abc.
Adding document with name abc to Application.
Pasting document named abc.
Now I know what you're thinking, 'WOW! Is that it!', but the command pattern offers a little more, to show this I created another command:
private Application application;
public SendCommand(Application application) {
this.application = application;
}
public void Execute() {
// breaking the 'Law of Demeter!'
string documentName = application.GetActiveDocument().GetName();
Console.WriteLine($"Sending {documentName}");
}
}
List
public MacroCommand() {
commands = new List
}
public void Add(ICommand command) {
commands.Add(command);
}
public void Remove(ICommand command) {
commands.Remove(command);
}
public void Execute() {
foreach (ICommand command in commands) {
command.Execute();
}
}
}
Anyrate here's an invoker (as a simple console application):
Application application = new Application();
// will remember the sequence, could also reverse
MacroCommand macroCommand = new MacroCommand();
// open three documents and send them, later in macroCommand.Execute();
for (int i = 0; i < 3; i++) {
macroCommand.Add(new OpenCommand(application));
macroCommand.Add(new SendCommand(application));
}
Console.WriteLine("\r\nCalling macroCommand.Execute()");
macroCommand.Execute();
Console.WriteLine("\r\n*****Done*****");
Console.ReadKey();
}
Document name?> document1
Creating and returning document named document1.
Adding document with name document1 to Application.
Sending document1
Document name?> document2
Creating and returning document named document2.
Adding document with name document2 to Application.
Sending document2
Document name?> document3
Creating and returning document named document3.
Adding document with name document3 to Application.
Sending document3
*****Done*****
Summary
There we have the command pattern and it's a great pattern for when we need to delay
or have undoable actions, for an invoker within a client - what does this mean?
Well the command is obvious but which is the client, which is the invoker and
which is the receiver? If we look at my example, compared to the structure
diagram then the receiver.action is called from the command so
Application is the Receiver and this is
created by the console application so this is the client and invoker as
it calls the command.execute method.
When we use the PasteCommand the receiver transfers to
the Document class, again leaving the client and invoker
to be the console application.
The MacroCommand, which is very similar to the
composite
pattern, has no receiver and is both a command and invoker.