Программирование на C++

10 способов прострелить себе ногу

Создание веб приложений

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

Подпись для третьего не придумал

Американец изнасиловал лошадь, потому что думал, что у них родится кентавр

Помощь обездоленным якутам на дальнем севере

Благотворительность (на правах рекламы)

воскресенье, 10 марта 2013 г.

Шаблон проектирования Команда (Command) + Реализация на C#

Существуют ситуации, когда у нас есть просто данные, над которыми нужно провести одну лишь единственную операцию, например, у нас есть кино на DVD диске и нам нужно всего лишь либо, запустить фильм, либо остановить, либо нажать на паузу и так далее. Также нам может понадобится такое действие, которое может отменить предыдущее (например операция undo в блокноте). Я могу написать функции (или методы), которые будут исполнять нашу команду и обрабатывать ее, но у нас будет очень много похожего кода, так как у нас не будет базового класса, который будет иметь поля схожие для всех команд. К тому же код будет очень не гибким. Фактически это КЛАСС, который инкапсулирует один единственный метод execute(). Это позволяет нам хранить команды в каких то контейнерах, обрабатывать их с помощью наших обработчиков.
(Пример стыбзен с википедии). Мы проектируем калькулятор со стандартными операциями сложения, деления, умножения, вычитания
Все строится сначала на классе Command:

abstract class Command
  {
    public abstract void Execute(); //метод выполнения команды
    public abstract void UnExecute(); //метод отмены
  }
Теперь делаем производные классы от команды. Причем этот класс должен хранить получателя(Calculator), класс, который принимает команды и обрабатывает их.

class CalculatorCommand : Command
  {
 char @operator; //оператор
 int operand;
 Calculator calculator; //ОБЪЕКТ ПОЛУЧАЮЩИЙ КОМАНДЫ (Receiver)
 
    // Constructor
 public CalculatorCommand(Calculator calculator,
    char @operator, int operand)
    {
      this.calculator = calculator;
      this.@operator = @operator;
      this.operand = operand;
    }
 
    public char Operator
    {
      set{ @operator = value; }
    }
 
    public int Operand
    {
      set{ operand = value; }
    }
 //здесь мы просто вызываем метод нашего получателя, на вход которого мы отправляем оператор и операнд
    public override void Execute() 
    {
      calculator.Operation(@operator, operand);
    }
 
    public override void UnExecute()
    {
      calculator.Operation(Undo(@operator), operand);
    }
 
    // Private helper function : приватные вспомогательные функции
    private char Undo(char @operator)
    {
      char undo;
      switch(@operator)
      {
        case '+': undo = '-'; break;
        case '-': undo = '+'; break;
        case '*': undo = '/'; break;
        case '/': undo = '*'; break;
        default : undo = ' '; break;
      }
      return undo;
    }
  }
Теперь нужен класс который будет получать команды и их обрабатывать.

// "Receiver" : получатель
 
  class Calculator
  {
    private int curr = 0;
 
    public void Operation(char @operator, int operand)
    {
      switch(@operator)
      {
        case '+': curr += operand; break;
        case '-': curr -= operand; break;
        case '*': curr *= operand; break;
        case '/': curr /= operand; break;
      }
      Console.WriteLine(
        "Current value = {0,3} (following {1} {2})",
        curr, @operator, operand);
    }
  }
Теперь нужен класс, который будет отдавать команды на обработку калькулятору..

class User
  {
    // Initializers
    private Calculator _calculator = new Calculator();
    private List _commands = new List();
 
    private int _current = 0;
 
    public void Redo(int levels)
    {
      Console.WriteLine("\n---- Redo {0} levels ", levels);
 
      // Делаем возврат операций
      for (int i = 0; i < levels; i++)
        if (_current < _commands.Count - 1)
          _commands[_current++].Execute();
    }
 
    public void Undo(int levels)
    {
      Console.WriteLine("\n---- Undo {0} levels ", levels);
 
      // Делаем отмену операций
      for (int i = 0; i < levels; i++)
        if (_current > 0)
          _commands[--_current].UnExecute();
    }
 
    public void Compute(char @operator, int operand)
    {
 
      // Создаем команду операции и выполняем её
      Command command = new CalculatorCommand(
        _calculator, @operator, operand);
      command.Execute();
 
      // Добавляем операцию к списку отмены
      _commands.Add(command);
      _current++;
    }
  }
Ну и пример работы с нашим калькулятором

class MainApp
  {
    static void Main()
    {
      // Создаем пользователя.
      User user = new User();
 
      // Пусть он что-нибудь сделает.
      user.Compute('+', 100);
      user.Compute('-', 50);
      user.Compute('*', 10);
      user.Compute('/', 2);
 
      // Отменяем 4 команды
      user.Undo(4);
 
      // Вернём 3 отменённые команды.
      user.Redo(3);
 
      // Ждем ввода пользователя и завершаемся.
      Console.Read();
    }
  }
Итак, теперь обобщим все, что мы здесь написали, вот схемка шаблона:
Ну, кто не силен в английском, могу объяснить, сначала наш вызывающий (по английски Invoker) (в нашем случае User), вызывает команду (причем команда передается в виде объекта) и посылает ее получателю (Calculator), где команда успешно обрабатывается калькулятором.
Благодаря такому шаблону, мы можем без труда можем добавить свои команды, которые с помощью метода execute() будут также выполнятся. Всего лишь надо унаследовать все от Command.

пятница, 1 марта 2013 г.

Сигналы и слоты в Qt

Сигналы и слоты в Qt - это механизм, который используются для взаимодействия между несколькими объектами. Фактически, сигналы и слоты помогают нам реализовать шаблон "Наблюдатель" (скоро описание этого шаблона будет добавлено в блог, если в скором времени не появится, пинайте меня, в C# это интерфейс System.IObserver<T>, в бусте boost.signals). Эта особенность существенно различает Qt от других библиотек.

Основная идея слотов и сигналов:
Когда событие происходит, то вырабатывается сигнал, который посылается на слот, слот это обработчик сигналов, фактически, это обычный метод класса (их даже можно делать виртуальными), который определяется после идентификатора public slots.
Предположим, что у нас есть класс MyClass:
(Не забываем, что для использования этого механизма нужен специальный макрос Q_OBJECT, который должен быть определен в теле класса, к тому же только классы наследуемые от QObject могут содержать слоты и сигналы, поэтому сделаем наследование
/////////////////////////////////////////////////////
class MyClass : public QObject {
    Q_OBJECT
//Определение
}
////////////////////////////////
Начнем с сигналов:
Как было сказано это тоже обычные методы:
////////////////////////////////////////////////////
class MyClass: public QObject {
Q_OBJECT
public:
...
signals:
   void mySignal();
};
///////////////////////////////
Для выработки сигнала используем ключевое слово emit:
/////////////////////////////////////////////////////////////////////////////////
void MyClass ::sendMySignal()
{
   emit mySignal();
}
/////////////////////////////////////////////////
Хочу сказать, что сигналы автоматически определяются и их НЕ НАДО определять, просто оставляем объявление.
Теперь слоты:
Как видим слоты - это обычные методы, у них есть определение, в отличие от сигналов, все они размещаются после ключевого слова - slots,причем мы указываем для них уровень доступа. Также можно добавлять аргументы (этот пример без аргумента)
/////////////////////////////////////////////////////////////////////////////////////////////
class MyClass: public QObject {
Q_OBJECT
 
public slots:
   void mySlot()
   {
      ...
   }
};
///////////////////////////////////////////////////////
Соединение сигнала и слота:
Для соединения сигнала и слота мы используем метод connect(sender, SIGNAL(signal), receiver, SLOT(slot))
Для нашего примера (т.е. сигнал и слот выше имена):
connect(sender, SIGNAL(MySignal()), receiver, SLOT(MySlot()));
Благодаря этому, при возникновении сигнала MySignal() автоматически вызовется метод MySlot(), который обработает сигнал MySignal().