Паттерн команда си шарп

Saved searches

Use saved searches to filter your results more quickly

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.

License

csharp-design-patterns/command

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Sign In Required

Please sign in to use Codespaces.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching Xcode

If nothing happens, download Xcode and try again.

Launching Visual Studio Code

Your codespace will open once ready.

There was a problem preparing your codespace, please try again.

Latest commit

Git stats

Files

Failed to load latest commit information.

README.md

It is a behavioral project pattern that transform a order to an independent object that contains all the necessary information.

  • Command: The own command, that contains the instructions and references.
  • Receiver: The thing that the command will execute.
  • Invoker: Responsible to execute the commands.
  • Client: The beginning of all. It decides what command will execute.

Command_Flow

An important observation is that the command has all the data to process the request.

I will not do specifics implementations like ClientRepository. Instead of this, I will just use the interface.

First we need to create the interface ICommand, which will contain all the methods that we will need:

public interface ICommand  void Execute(); bool CanExecute(); void Undo(); >

Note that we have three methods:

  • Execute(): to execute, in fact, the command
  • CanExecute(): some condition that we consider necessary to execute the command
  • Undo(): like rollback to the command

Next, let’s create a Command Manager, it will responsible to invoke all the commands that we will pass for it:

public class CommandManager  private StackICommand> commands = new StackICommand>(); public void Invoke(ICommand command)  if (command.CanExecute())  commands.Push(command); command.Execute(); > > public void Undo()  while(commands.Count > 0)  var command = commands.Pop(); command.Undo(); > > >

And finally, a specific command:

public class AddClientCommand : ICommand  private readonly IClientRepository _clientRepository; private readonly Client _client; public AddClientCommand(IClientRepository clientRepository, Client client) => (_clientRepository, _client) = (clientRepository, client); public bool CanExecute()  if (_client == null) return false; return _clientRepository.GetClient(_client.Id) == null; > public void Execute()  if (_client == null) return; _clientRepository.Add(_client); > public void Undo()  if (_client == null) return; _clientRepository.Remove(_client.Id); > >

Well, this command is responsible to insert/create a client in our data store, like its name already show. Let’s analyze this code:

  1. We receive, by constructor, all we need:
    1. The repository (IClientRepository), that we use to do some operation in the datastore
    2. And the Client object that we will use to do something

    We pass the responsibility to create the client for another part of our project, let’s see how can we call the commands for this:

    class Program  static void Main(string[] args)  IClientRepository clientRepository = default; Client client = default; var addClientCommand = new AddClientCommand(clientRepository, client); var manager = new CommandManager(); manager.Invoke(addClientCommand); // . do something manager.Undo(); > >
    • First, we instantiate our Client Command, passing the repository and the object.
    • The AddClientCommand is of the ICommand type, so we can pass it to our CommandManager, to execute the command.
    • Finally, if we need to undo our process we can call the Undo() method, of the CommandManager

    This is very useful too if we work with an online store. For example, in the online store we can add many items to a cart, right? So, we can make many commands to do the add item in cart operation. And, if in a moment we can empty this cart, we just call the Undo method.

    The ICommand interface, that we built, already exists natively. You just call the System.Windows.Input. You can use it too

    • Allow you to make a separate responsibilities.
    • Make your application more robust.
    • Pay attention for you do not to put unnecessary complexity.

    Источник

    Паттерн команда си шарп

    Паттерн «Команда» (Command) позволяет инкапсулировать запрос на выполнение определенного действия в виде отдельного объекта. Этот объект запроса на действие и называется командой. При этом объекты, инициирующие запросы на выполнение действия, отделяются от объектов, которые выполняют это действие.

    Команды могут использовать параметры, которые передают ассоциированную с командой информацию. Кроме того, команды могут ставиться в очередь и также могут быть отменены.

    Когда использовать команды?

    Когда надо передавать в качестве параметров определенные действия, вызываемые в ответ на другие действия. То есть когда необходимы функции обратного действия в ответ на определенные действия.

    Когда необходимо обеспечить выполнение очереди запросов, а также их возможную отмену.

    Когда надо поддерживать логгирование изменений в результате запросов. Использование логов может помочь восстановить состояние системы — для этого необходимо будет использовать последовательность запротоколированных команд.

    Схематично в UML паттерн Команда представляется следующим образом:

    Паттерн Команда в C#

    Формальное определение на языке C# может выглядеть следующим образом:

    abstract class Command < public abstract void Execute(); public abstract void Undo(); >// конкретная команда class ConcreteCommand : Command < Receiver receiver; public ConcreteCommand(Receiver r) < receiver = r; >public override void Execute() < receiver.Operation(); >public override void Undo() <> > // получатель команды class Receiver < public void Operation() < >> // инициатор команды class Invoker < Command command; public void SetCommand(Command c) < command = c; >public void Run() < command.Execute(); >public void Cancel() < command.Undo(); >> class Client < void Main() < Invoker invoker = new Invoker(); Receiver receiver = new Receiver(); ConcreteCommand command=new ConcreteCommand(receiver); invoker.SetCommand(command); invoker.Run(); >>

    Участники

    • Command : интерфейс, представляющий команду. Обычно определяет метод Execute() для выполнения действия, а также нередко включает метод Undo() , реализация которого должна заключаться в отмене действия команды
    • ConcreteCommand : конкретная реализация команды, реализует метод Execute() , в котором вызывается определенный метод, определенный в классе Receiver
    • Receiver : получатель команды. Определяет действия, которые должны выполняться в результате запроса.
    • Invoker : инициатор команды — вызывает команду для выполнения определенного запроса
    • Client : клиент — создает команду и устанавливает ее получателя с помощью метода SetCommand()

    Таким образом, инициатор, отправляющий запрос, ничего не знает о получателе, который и будет выполнять команду. Кроме того, если нам потребуется применить какие-то новые команды, мы можем просто унаследовать классы от абстрактного класса Command и реализовать его методы Execute и Undo.

    В программах на C# команды находят довольно широкое применение. Так, в технологии WPF и других технологиях, которые используют XAML и подход MVVM, на командах во многом базируется взаимодействие с пользователем. В некоторых архитектурах, например, в архитектуре CQRS, команды являются одним из ключевых компонентов.

    Нередко в роли инициатора команд выступают панели управления или кнопки интерфейса. Самая простая ситуация — надо программно организовать включение и выключение прибора, например, телевизора. Решение данной задачи с помощью команд могло бы выглядеть так:

    class Program < static void Main(string[] args) < Pult pult = new Pult(); TV tv = new TV(); pult.SetCommand(new TVOnCommand(tv)); pult.PressButton(); pult.PressUndo(); Console.Read(); >> interface ICommand < void Execute(); void Undo(); >// Receiver - Получатель class TV < public void On() < Console.WriteLine("Телевизор включен!"); >public void Off() < Console.WriteLine("Телевизор выключен. "); >> class TVOnCommand : ICommand < TV tv; public TVOnCommand(TV tvSet) < tv = tvSet; >public void Execute() < tv.On(); >public void Undo() < tv.Off(); >> // Invoker - инициатор class Pult < ICommand command; public Pult() < >public void SetCommand(ICommand com) < command = com; >public void PressButton() < command.Execute(); >public void PressUndo() < command.Undo(); >>

    Итак, в этой программе есть интерфейс команды — ICommand, есть ее реализация в виде класса TVOnCommand, есть инициатор команды — класс Pult, некий прибор — пульт, управляющий телевизором. И есть получатель команды — класс TV, представляющий телевизор. В качестве клиента используется класс Program.

    При этом пульт ничего не знает об объекте TV. Он только знает, как отправить команду. В итоге мы получаем гибкую систему, в которой мы легко можем заменять одни команды на другие, создавать последовательности команд. Например, в нашей программе кроме телевизора появилась микроволновка, которой тоже неплохо было бы управлять с помощью одного интерфейса. Для этого достаточно добавить соответствующие классы и установить команду:

    class Program < static void Main(string[] args) < Pult pult = new Pult(); TV tv = new TV(); pult.SetCommand(new TVOnCommand(tv)); pult.PressButton(); pult.PressUndo(); Microwave microwave = new Microwave // 5000 - время нагрева пищи pult.SetCommand(new MicrowaveCommand(microwave, 5000)); pult.PressButton(); Console.Read(); >> //. ранее описанные классы class Microwave < public void StartCooking(int time) < Console.WriteLine("Подогреваем еду"); // имитация работы с помощью асинхронного метода Task.Delay Task.Delay(time).GetAwaiter().GetResult(); >public void StopCooking() < Console.WriteLine("Еда подогрета!"); >> class MicrowaveCommand : ICommand < Microwave microwave; int time; public MicrowaveCommand(Microwave m, int t) < microwave = m; time = t; >public void Execute() < microwave.StartCooking(time); microwave.StopCooking(); >public void Undo() < microwave.StopCooking(); >>

    Теперь еще одним получателем запроса является класс Microwave, функциональностью которого можно управлять через команды MicrowaveCommand.

    Правда, в вышеописанной системе есть один изъян: если мы попытаемся выполнить команду до ее назначения, то программа выдаст исключение, так как команда будет не установлена. Эту проблему мы могли бы решить, проверяя команду на значение null в классе инициатора:

    class Pult < ICommand command; public Pult() < >public void SetCommand(ICommand com) < command = com; >public void PressButton() < if(command!=null) command.Execute(); >public void PressUndo() < if(command!=null) command.Undo(); >>

    Либо можно определить класс пустой команды, которая будет устанавливаться по умолчанию:

    class NoCommand : ICommand < public void Execute() < >public void Undo() < >> class Pult < ICommand command; public Pult() < command = new NoCommand(); >public void SetCommand(ICommand com) < command = com; >public void PressButton() < command.Execute(); >public void PressUndo() < command.Undo(); >>

    При этом инициатор необязательно указывает на одну команду. Он может управлять множеством команд. Например, на пульте от телевизора есть как кнопка для включения, так и кнопки для регулировки звука:

    class Program < static void Main(string[] args) < TV tv = new TV(); Volume volume = new Volume(); MultiPult mPult = new MultiPult(); mPult.SetCommand(0, new TVOnCommand(tv)); mPult.SetCommand(1, new VolumeCommand(volume)); // включаем телевизор mPult.PressButton(0); // увеличиваем громкость mPult.PressButton(1); mPult.PressButton(1); mPult.PressButton(1); // действия отмены mPult.PressUndoButton(); mPult.PressUndoButton(); mPult.PressUndoButton(); mPult.PressUndoButton(); Console.Read(); >> interface Command < void Execute(); void Undo(); >class TV < public void On() < Console.WriteLine("Телевизор включен!"); >public void Off() < Console.WriteLine("Телевизор выключен. "); >> class TVOnCommand : ICommand < TV tv; public TVOnCommand(TV tvSet) < tv = tvSet; >public void Execute() < tv.On(); >public void Undo() < tv.Off(); >> class Volume < public const int OFF = 0; public const int HIGH = 20; private int level; public Volume() < level = OFF; >public void RaiseLevel() < if (level < HIGH) level++; Console.WriteLine("Уровень звука ", level); > public void DropLevel() < if (level >OFF) level--; Console.WriteLine("Уровень звука ", level); > > class VolumeCommand : ICommand < Volume volume; public VolumeCommand(Volume v) < volume = v; >public void Execute() < volume.RaiseLevel(); >public void Undo() < volume.DropLevel(); >> class NoCommand : ICommand < public void Execute() < >public void Undo() < >> class MultiPult < ICommand[] buttons; StackcommandsHistory; public MultiPult() < buttons = new ICommand[2]; for (int i = 0; i < buttons.Length; i++) < buttons[i] = new NoCommand(); >commandsHistory = new Stack(); > public void SetCommand(int number, ICommand com) < buttons[number] = com; >public void PressButton(int number) < buttons[number].Execute(); // добавляем выполненную команду в историю команд commandsHistory.Push(buttons[number]); >public void PressUndoButton() < if(commandsHistory.Count>0) < ICommand undoCommand = commandsHistory.Pop(); undoCommand.Undo(); >> >

    Здесь два получателя команд — классы TV и Volume. Volume управляет уровнем звука и сохраняет текущий уровень в переменной level. Также есть две команды TVOnCommand и VolumeCommand.

    Инициатор — MultiPult имеет две кнопки в виде массива buttons: первая предназначена для TV, а вторая — для увеличения уровня звука. Чтобы сохранить историю команд используется стек. При отправке команды в стек добавляется новый элемент, а при ее отмене, наоборот, происходит удаление из стека. В данном случае стек выполняет роль примитивного лога команд.

    Телевизор включен! Уровень звука 1 Уровень звука 2 Уровень звука 3 Уровень звука 2 Уровень звука 1 Уровень звука 0 Телевизор выключен.

    Макрокоманды

    Для управления набором команд используются макрокоманды. Макрокоманда должна реализовать тот же интерфейс, что и другие команды, при этом макрокоманда инкапсулирует в одной из своих переменных весь набор используемых команд. Рассмотрим на примере.

    Для создания и развития программного продукта необходимо несколько исполнителей, выполняющих различные функции: программист пишет код, тестировщик выполняет тестирование продукта, а маркетолог пишет рекламные материалы и проводит кампании по рекламированию продукта. Управляет всем процессом менеджер. Программа на C#, описывающая создание программного продукта с помощью паттерна команд, могла бы выглядеть следующим образом:

    class Program < static void Main(string[] args) < Programmer programmer = new Programmer(); Tester tester = new Tester(); Marketolog marketolog = new Marketolog(); Listcommands = new List < new CodeCommand(programmer), new TestCommand(tester), new AdvertizeCommand(marketolog) >; Manager manager = new Manager(); manager.SetCommand(new MacroCommand(commands)); manager.StartProject(); manager.StopProject(); Console.Read(); > > interface ICommand < void Execute(); void Undo(); >// Класс макрокоманды class MacroCommand : ICommand < Listcommands; public MacroCommand(List coms) < commands = coms; >public void Execute() < foreach(ICommand c in commands) c.Execute(); >public void Undo() < foreach (ICommand c in commands) c.Undo(); >> class Programmer < public void StartCoding() < Console.WriteLine("Программист начинает писать код"); >public void StopCoding() < Console.WriteLine("Программист завершает писать код"); >> class Tester < public void StartTest() < Console.WriteLine("Тестировщик начинает тестирование"); >public void StopTest() < Console.WriteLine("Тестировщик завершает тестирование"); >> class Marketolog < public void StartAdvertize() < Console.WriteLine("Маркетолог начинает рекламировать продукт"); >public void StopAdvertize() < Console.WriteLine("Маркетолог прекращает рекламную кампанию"); >> class CodeCommand : ICommand < Programmer programmer; public CodeCommand(Programmer p) < programmer = p; >public void Execute() < programmer.StartCoding(); >public void Undo() < programmer.StopCoding(); >> class TestCommand : ICommand < Tester tester; public TestCommand(Tester t) < tester = t; >public void Execute() < tester.StartTest(); >public void Undo() < tester.StopTest(); >> class AdvertizeCommand : ICommand < Marketolog marketolog; public AdvertizeCommand(Marketolog m) < marketolog = m; >public void Execute() < marketolog.StartAdvertize(); >public void Undo() < marketolog.StopAdvertize(); >> class Manager < ICommand command; public void SetCommand(ICommand com) < command = com; >public void StartProject() < if (command != null) command.Execute(); >public void StopProject() < if (command != null) command.Undo(); >>

    В роли инициатора здесь выступает менеджер, а в роли получателей запросов — программист, маркетолог и тестеровщик. Запуская проект, менеджер тем самым запускает макрокоманду, которая содержит ряд отдельных команд. Выполнение этих команд делегируется получателям.

    Источник

    Читайте также:  Sockettimeoutexception java net sockettimeoutexception read timed out
Оцените статью