Команда (шаблон проєктування)Команда (англ. Command) — шаблон проєктування, відноситься до класу шаблонів поведінки. Також відомий як Дія (англ. Action), Транзакція (англ. Transaction). ПризначенняІнкапсулює запит у формі об'єкта, дозволяючи тим самим задавати параметри клієнтів для обробки відповідних запитів, ставити запити у чергу або протоколювати їх, а також підтримувати скасовування операцій. МотиваціяСтворення структури, в якій клас-відправник і клас-отримувач не залежать один від одного напряму. Організація зворотного виклику до класу, який містить у собі клас-відправник. ЗастосовністьСлід використовувати шаблон Команда коли:
Структура
Відносини
На діаграмі видно, як Command розриває зв'язок між викликачем та отримувачем (а також запитом, що повинен бути виконаний останнім). Переваги
Недоліки
РеалізаціяC++Приклад реалізації на мові С++
#include <iostream>
#include <vector>
using namespace std;
struct Command // Основа патерну
{
virtual void execute() = 0;
};
// Уточнення
struct Hello : public Command
{
virtual void execute() { cout << " Hello "; };
};
struct World : public Command
{
virtual void execute() { cout << " World!"; };
};
struct IAm : public Command
{
virtual void execute()
{
cout << " I am the command pattern!";
}
};
// Місце, де застосовують команду
class Macro
{
private:
vector< Command*> commands;
public:
void add(Command* c) { commands.push_back(c); }
void run()
{
vector< Command*> ::iterator it = commands.begin();
while (it != commands.end()) (*it++)->execute();
}
};
void main()
{
Macro macro;
macro.add(new Hello);
macro.add(new World);
macro.add(new IAm);
macro.run();
}
C#Приклад реалізації на мові С#
using System;
using System.Linq;
using System.Collections.Generic;
namespace Command
{
// основа патерну
public interface ICommand
{
string Name { get; }
void Execute();
void UnExecute();
}
// різноманітні команди
class ChangeColorCommand : ICommand
{
// стан об'єкта до і після застосування команди
private readonly ConsoleColor newColor;
private readonly ConsoleColor prevColor;
public ChangeColorCommand(ConsoleColor newColor)
{
this.newColor = newColor;
this.prevColor = Console.ForegroundColor;
}
public string Name => $"Change foreground color to {newColor}";
public void Execute()
{
Console.ForegroundColor = newColor;
}
public void UnExecute()
{
Console.ForegroundColor = prevColor;
}
}
class ChangeBackColorCommand : ICommand
{
private readonly ConsoleColor newColor;
private readonly ConsoleColor prevColor;
public ChangeBackColorCommand(ConsoleColor newColor)
{
this.newColor = newColor;
this.prevColor = Console.BackgroundColor;
}
public string Name => $"Change background color to {newColor}";
public void Execute()
{
Console.BackgroundColor = newColor;
}
public void UnExecute()
{
Console.BackgroundColor = prevColor;
}
}
// Місце, де застосовують команди
class UndoRedoManager
{
// історії команд
Stack<ICommand> undoStack;
Stack<ICommand> redoStack;
public event EventHandler StateChanged;
public UndoRedoManager()
{
undoStack = new Stack<ICommand>();
redoStack = new Stack<ICommand>();
}
public bool CanUndo => undoStack.Count > 0;
public bool CanRedo => redoStack.Count > 0;
public void Undo()
{
if (CanUndo)
{
ICommand command = undoStack.Pop();
command.UnExecute();
redoStack.Push(command);
StateChanged?.Invoke(this, EventArgs.Empty);
}
}
public void Redo()
{
if (CanRedo)
{
ICommand command = redoStack.Pop();
command.Execute();
undoStack.Push(command);
StateChanged?.Invoke(this, EventArgs.Empty);
}
}
// усі команди виконуються через метод Execute
public void Execute(ICommand command)
{
command.Execute();
undoStack.Push(command);
redoStack.Clear();
StateChanged?.Invoke(this, EventArgs.Empty);
}
public IEnumerable<string> UndoItems => undoStack.Select(c => c.Name);
public IEnumerable<string> RedoItems => redoStack.Select(c => c.Name);
public void Undo(int count)
{
for (int i = 0; i < count; ++i) Undo();
}
public void Redo(int count)
{
for (int i = 0; i < count; ++i) Redo();
}
}
class Program
{
// напишемо фасад для легшого керування системою
class Menu
{
// FIELDS
UndoRedoManager undoRedoManager;
bool exit;
// CONSTRUCTORS
public Menu()
{
undoRedoManager = new UndoRedoManager();
}
// METHODS
public void Run()
{
while (!exit)
{
ShowMenuItems();
int userChoice = GetInput();
Perform(userChoice);
}
}
private void ShowMenuItems()
{
Console.Clear();
Console.WriteLine("Choose option");
Console.WriteLine();
Console.WriteLine("0 - Get undo commands list");
Console.WriteLine("1 - Get redo commands list");
Console.WriteLine("2 - Change foreground color");
Console.WriteLine("3 - Change background color");
Console.WriteLine("4 - Undo");
Console.WriteLine("5 - Redo");
Console.WriteLine("6 - Exit");
Console.WriteLine();
}
private int GetInput()
{
// get user choice
int userChoice;
do
{
Console.WriteLine("Your input:");
} while (!int.TryParse(Console.ReadLine(), out userChoice));
return userChoice;
}
private void Perform(int userChoice)
{
switch (userChoice)
{
case 0: GetUndoCommandList(); break;
case 1: GetRedoCommandList(); break;
case 2: ChangeForegroundColor(); break;
case 3: ChangeBackgroundColor(); break;
case 4: Undo(); break;
case 5: Redo(); break;
case 6: Exit(); break;
default: Console.WriteLine("Wrong choice"); break;
}
Console.WriteLine("Press enter");
Console.ReadLine();
}
// ACTIONS
private void GetUndoCommandList()
{
Console.WriteLine("Undo list:");
foreach(string commandName in undoRedoManager.UndoItems)
{
Console.WriteLine(commandName);
}
}
private void GetRedoCommandList()
{
Console.WriteLine("Redo list:");
foreach (string commandName in undoRedoManager.RedoItems)
{
Console.WriteLine(commandName);
}
}
private void ChangeForegroundColor()
{
// get user input
ConsoleColor newForegroundColor;
string color = string.Empty;
do
{
Console.WriteLine("Write new color");
color = Console.ReadLine();
} while (!Enum.TryParse(color, out newForegroundColor));
// execute command
undoRedoManager.Execute(new ChangeColorCommand(newForegroundColor));
}
private void ChangeBackgroundColor()
{
// get user input
ConsoleColor newBackgroundColor;
string color = string.Empty;
do
{
Console.WriteLine("Write new color");
color = Console.ReadLine();
} while (!Enum.TryParse(color, out newBackgroundColor));
// execute command
undoRedoManager.Execute(new ChangeBackColorCommand(newBackgroundColor));
}
private void Undo()
{
undoRedoManager.Undo();
}
private void Redo()
{
undoRedoManager.Redo();
}
private void Exit()
{
exit = true;
}
}
static void Main(string[] args)
{
new Menu().Run();
}
}
}
Swift[1]Приклад реалізації на мові Swift
protocol DoorCommand {
func execute() -> String
}
class OpenCommand : DoorCommand {
let doors:String
required init(doors: String) {
self.doors = doors
}
func execute() -> String {
return "Opened \(doors)"
}
}
class CloseCommand : DoorCommand {
let doors:String
required init(doors: String) {
self.doors = doors
}
func execute() -> String {
return "Closed \(doors)"
}
}
class HAL9000DoorsOperations {
let openCommand: DoorCommand
let closeCommand: DoorCommand
init(doors: String) {
self.openCommand = OpenCommand(doors:doors)
self.closeCommand = CloseCommand(doors:doors)
}
func close() -> String {
return closeCommand.execute()
}
func open() -> String {
return openCommand.execute()
}
}
let podBayDoors = "Pod Bay Doors"
let doorModule = HAL9000DoorsOperations(doors:podBayDoors)
doorModule.open()
doorModule.close()
Джерела
Посилання
ЛітератураАлан Шаллоуей, Джеймс Р. Тротт. Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М. : «Вильямс», 2002. — 288 с. — ISBN 0-201-71594-5.
|