Паттерны проектирования: Команда (Command)

Команда (Command) — поведенческий шаблон проектирования. Позволяет инкапсулировать запрос в виде объекта, позволяя передавать их клиентам в качестве параметров, ставить в очередь, логировать и поддерживать отмену операций.

Простейшая схема работы паттерна:

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

Для начала создадим интерфейс ICommand, он содержит действия для выполнения команды и её отмены.

 
public interface ICommand
    {
        void Execute();
        void Undo();
    }

Для удобства хранения состояния устройств, который работают в двух состояниях (включён/выключен), создадим перечисление State

 
  public enum State
    {
        Off = 0,
        On = 1
    }

Теперь перейдём к созданию класса, отвечающего за телевизор:

 
    public class Tv
    {
        public void TurnOn()
        {
            Console.WriteLine("Телевизор включен");
            State = State.On;
        }
        public void TurnOf()
        {
            Console.WriteLine("Телевизор выключен");
            State = State.Off;
        }
        public State State { get; set; }
    }

Класс содержит методы для включения телевизора (TurnOn) и его выключения (TurnOff). Текущее состояние хранится в State и по умолчанию равно Off.

Класс для работы с телевизором, будет наследоваться от интерфейса ICommand и реализует методы Execute и Undo

 
class CommandTv : ICommand
    {
        private readonly Tv _tv;
        private string _name;
        public CommandTv(String name)
        {
            _tv = new Tv();
            _name = name;
        }

        public CommandTv(Tv tv, String name)
        {
            _tv = tv;
            _name = name;
        }

        public void Execute()
        {
            switch (_tv.State)
            {
                case State.On:

                    _tv.TurnOf();
                    break;
                case State.Off:

                    _tv.TurnOn();
                    break;
            }
        }

        public void Undo()
        {
            switch (_tv.State)
            {
                case State.On:
                    _tv.TurnOf();
                    break;
                case State.Off:
                    _tv.TurnOn();
                    break;
            }
        }

        public override string ToString()
        {
            return _name;
        }
    }

Перегруженный метод ToString() будет показывать, чем конкретно происходит управление.

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

Если объект может выполнять множество различных команд, то имеет смысл хранить выполненные команды в списке или стеке.

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

 
public class RemoteControl
    {
        private readonly Dictionary<int, icommand=""> _devices;

        public RemoteControl()
        {
            _devices = new Dictionary<int, icommand="">();
        }

        public void AddDevice(int id, ICommand device)
        {
            _devices[id] = device;
        }

        public void RunCommand(int id)
        {
            if (_devices.ContainsKey(id))
            {
                _devices[id].Execute();
            }
        }

        public void RemoveDevice(ICommand device)
        {
            if (_devices.ContainsValue(device))
            {
                var removeDevice = _devices.FirstOrDefault(x => x.Value == device);
                _devices.Remove(removeDevice.Key);
            }
        }

        /// 
        /// Отображение списка привязанных к кнопкам устройств
        /// 
        public void PrintMenu()
        {
            foreach (var command in _devices)
            {
                Console.WriteLine("{0}: \t {1}", command.Key, command.Value);
            }
            Console.WriteLine("0: \t ВЫХОД");
        }
    }

Осталось протестировать работу проекта:

 
            var tv = new CommandTv("Телевизор в гостиной");
            var tv2 = new CommandTv("Телевизор в спальне");
            var tv3 = new CommandTv("Телевизор на кухне");

            var light = new CommandLight("Свет в гостиной");
            var light2 = new CommandLight("Свет в спальне");
            var light3 = new CommandLight("Свет на кухне");
            

            var remoteControl = new RemoteControl();
            remoteControl.AddDevice(1, tv);
            remoteControl.AddDevice(2, light);
            remoteControl.AddDevice(3, tv2);
            remoteControl.AddDevice(4, light2);
            remoteControl.AddDevice(5, tv3);
            remoteControl.AddDevice(6, light3);
           
            remoteControl.PrintMenu();

            var input = Console.ReadLine();


            while (input != "0")
            {
                if (input != null)
                {
                    var button = Int32.Parse(input);
                    remoteControl.RunCommand(button);
                }
                input = Console.ReadLine();
            }

Результат работы будет следующий:

 

Исходный код учебного проекта: https://github.com/flash2048/Patterns/tree/master/Command

Добавить комментарий