English 中文 Español Deutsch 日本語 Português
preview
Объектно-ориентированное программирование (ООП) в MQL5

Объектно-ориентированное программирование (ООП) в MQL5

MetaTrader 5Трейдинг | 21 сентября 2023, 16:38
1 317 6
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

Введение

В этой статье мы поделимся одной из самых важных тем в программировании, облегчающую написание кода и позволяет применить принцип DRY (Do Not Repeat Yourself, не повторяйся) разработчикам и программистам. Кроме того, помимо прочего повышается безопасность любого созданного программного обеспечения. Мы поговорим об объектно-ориентированном программировании (ООП) и о том, как мы можем использовать эту концепцию при работе с MQL5 (MetaQuotes Language).

Итак, мы раскроем эту интересную и важную тему в следующих разделах:

Основная цель этой статьи — дать понимание основ объектно-ориентированного программирования (ООП) в целом и того, как оно может быть использовано при создании программного обеспечения. Мы узнаем, как можно применить этот подход в MQL5 для создания более эффективных и безопасных программ.

При необходимости вы можете найти информативные ресурсы об этом подходе, тем более что он применим в других языках программирования, таких как C++, Java, Python и других. Всю необходимую информацию о языке MQL5 можно найти в официальной документации.

Внимание! Все содержание настоящей статьи предоставляется "как есть", предназначено только для целей обучения и не является торговой рекомендацией. Статья не несет в себе каких-либо гарантий результатов. Все, что вы применяете на практике на основе этой статьи, вы делаете исключительно на свой страх и риск, автор не гарантирует никаких результатов.


Что такое ООП?

Начнем с определения ООП. ООП помогает создавать и разрабатывать повторно используемое программное обеспечение без дублирования работы и кода с применением концепции DRY (не повторяйся).

ООП помогает нам приблизиться к природе мира, потому что объекты окружают нас повсюду в повседневной жизни. Мы будем иметь дело с объектами и классами, объект означает экземпляр класса, а класс — это шаблон для объекта. Внутри шаблона, который является классом, мы подробно определяем поведение объекта.

Основные понятия ООП:

При использовании ООП в разработке применяются следующие принципы.

  1. Инкапсуляция.
  2. Абстракция.
  3. Наследование.
  4. Полиморфизм.

ооп

1. Инкапсуляция:

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

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

Следующий рисунок описывает понятие инкапсуляции:

Инкапсуляция

2. Абстракция:

Абстракция – это метод скрытия ненужных и представления только существенных деталей. Она шире понятия инкапсуляции, но помогает достичь той же цели: защитить данные и реализовать функции без полного знания о процессе реализации всех классов, а только с пониманием того, что нужно сделать, чтобы реализация была выполнена.

Чтобы достичь этой цели нам нужно включить два важных метода - интерфейс и реализацию. Интерфейс — это метод, который позволяет классам взаимодействовать друг с другом, а реализация — это метод, который содержит все детали кода или логику классов. Таким образом, абстракция помогает повысить безопасность программного обеспечения, а также позволяет не повторять процесс кодирования с нуля, разрабатывая и кодируя больше приложений на основе уже созданных.

3. Наследование:

Судя по названию, концепция наследования означает, что мы получаем новый класс от старого, при этом новый наследует функции старого. В этом случае старый класс называется родительским или суперклассом, а новый производный класс называется дочерним классом. Эта концепция помогает применить концепцию DRY (не повторяйся), при этом сохраняя возможность повторного использования.

Наследование

4. Полиморфизм:

Полиморфизм позволяет функции обрабатывать данные разных типов. Например, мы можем использовать метод суммирования для получения значений суммы (a) и (b) и другой - для получения суммы (a), (b) и (c).

Проще говоря, полиморфизм означает один интерфейс и множество методов.

Резюмируя, можно сказать, что ООП — это модель программирования, которая фокусируется на организации разработки программного обеспечения с помощью объектов, которые могут иметь уникальное поведение и характеристики. Это полезная модель в больших и сложных программах, особенно если они часто обновляются.

Особенности ООП:

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

Существует множество языков программирования, которые могут применять подход ООП. Наиболее популярными являются C++, C#, Python, Java, JavaScript, PHP и другие. В их число также входит и MQL5.


ООП в MQL5

В этом разделе мы рассмотрим ООП в MQL5 и узнаем, как его использовать.

Перед применением ООП в MQL5, нам нужно понять, как мы можем использовать в MQL5 следующее:

  • Классы
  • Модификатор доступа
  • Конструкторы и деструкторы
  • Производные (дочерние) классы
  • Виртуальные функции
  • Объекты

Классы:

В MQL5, если нам нужно создать класс, который будет образцом объекта, нам нужно объявить этот класс в глобальной области видимости так же, как функции. Мы можем создать этот класс, используя ключевое слово class, за которым следует желаемый уникальный идентификатор, затем внутри фигурных скобок мы можем разместить наши переменные и методы, которые являются членами класса, а затем после второй фигурной скобки мы ставим точку с запятой, чтобы завершить объявление класса. Мы можем использовать это объявление класса в программе или во включаемом файле.

Ниже приведен пример объявления этого класса:

class Cobject
{
   int var1;       // variable1
   double var2;    // variable1
   void method1(); // Method or function1
};

Как мы видим в предыдущем примере, у нас есть три члена класса: две переменные и один метод или функция.

Модификаторы доступа:

С помощью этих модификаторов доступа мы можем определить, какие переменные и функции мы можем использовать вне класса, и у нас есть три ключевых слова доступа: public, private и protected.

  • Public: члены, которые могут быть доступны для использования вне класса.
  • Private: представляет члены, которые не могут быть доступны для использования вне класса, но доступны для использования внутри класса только функциями. Дочерний класс этого класса не будет наследовать эти приватные члены.
  • Protected: представляет элементы, которые будут унаследованы дочерними классами, но являются приватными по своей природе.

Ниже представлен пример:

class Cobject
{
   private:
   int var1;       // variable1
   protected:
   double var2;    // variable1
   public:
   void method1(); // Method or function1
};

Как мы видим в предыдущем примере, у нас есть три члена в классе с двумя переменными: один является приватным (private), другой - защищенным (protected), а третий - общедоступным (public).

Конструкторы и деструкторы:

Если нам нужно инициализировать переменные в классе, мы используем конструктор. При отсутствии он будет создан компилятором по умолчанию, но по умолчанию не будет виден. Он также должен быть общедоступным. Деструктор — это автоматически вызываемая функция при уничтожении объекта класса. Деструктор можно назвать так же, как имя класса, с тильдой (~). Независимо от того, имеется ли деструктор или нет, строка, динамический массив и объект требуют деинициализации, поэтому они будут деинициализированы в любом случае.

Ниже приведен пример конструктора:

class CPrices
  {
private:
   double               open;         // Open price
   double               high;         // High price
   double               low;          // Low price
   double               close;        // Close price
public:
   //--- Default constructor
                     CPrices(void);
   //--- Parametric constructor
                     CPrices(double o,double h,double l, double c);
  };

Производные (дочерние) классы:

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

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

class CDailyPrices : public CPrices
{
public:
   double               open;          // Open price
   double               high;          // High price
   double               low;           // Low price
   double               close;         // Close price
};

Как мы видим, имя родительского класса — CPrices, а CDailyPrices — дочерний, или производный класс. Все общедоступные и защищенные члены CPrices являются частью класса CDailyPrices и по-прежнему являются общедоступными.

Виртуальные функции:

Если мы хотим обновить способ работы метода или функции в дочернем классе, мы можем сделать это, используя (виртуальную) функцию в родительском классе, а затем определить функцию в дочернем классе. Например, если у нас есть две разные версии функции на основе класса. Для родительского класса мы определяем функцию, используя ключевое слово virtual

class CVar
  {
public:
   virtual 
void varCal();
  };

Затем мы обновляем ту же функцию в дочернем классе.

class CVar1 : public CVar
{
public:
 int varCal(int x, int y);
}; 

Объекты:

Объекты имеют уникальный идентификатор. Как и при создании переменной, мы будем использовать имя класса в качестве типа перед идентификатором объекта. Мы можем создать множество объектов, принадлежащих нашим классам, так как для проекта нам нужно всего лишь использовать уникальный идентификатор для каждого. После объявления объекта мы можем получить доступ к любому публичному члену, используя (.) - точку.

Давайте рассмотрим пример, показывающий создание класса с целочисленной переменной количества сделок (num_trades),

class CSystrades
{
public:
int num_trades;
};

Затем нам нужно создать объект, принадлежащий этому классу, с именем system1. Мы сделаем это, выполнив следующие действия:

CSystrades system1;

Тогда мы можем определить этот объект по значению (3):

system1.num_trades=3;

Мы рассмотрели, как можно применить ООП в MQL5, изучив некоторые из наиболее важных моментов.

Примеры ООП

В этом интересном разделе мы представим несколько простых приложений, применяющих ООП.

priceClass:

В этом простом приложении нам нужно проверить цены на нескольких таймфреймах. Мы используем три таймфрейма (дневной, недельный и месячный). Нам также нужно увидеть все цены (открытие, максимум, минимум, закрытие) в одном месте, скажем, на вкладке "Эксперты". После этого мы можем продолжить разработку более сложных программ.

Во-первых, нам нужно объявить класс, выполнив следующие шаги:

  • Нам нужно объявить класс для цен в глобальной области видимости и включить все общие члены как общедоступные, используя ключевое слово class.
  • Использовать ключевое слово public.
  • Создать пять переменных (timeframe, open, high, low и close).
  • Создать функцию void для отображения всех данных о ценах.
class CPrices
  {
public:
   string            timeFrame;
   double            open;
   double            high;
   double            low;
   double            close;
   void              pricesPrint()
     {
      Print(timeFrame," Prices = Open: ",open," - ","High: ",high,"-","Low: ",low,"-","Close: ",close);
     }
  };

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

CPrices CDailyPrices;
CPrices CWeeklyPrices;
CPrices CMonthlyPrices;

Внутри функции OnInit определим следующее для трех таймфреймов:

  • Строку с названием таймфрейма.
  • Цену открытия с помощью функции iOpen.
  • Максимальную цену с помощью функции iHigh.
  • Минимальную цену с помощью функции iLow.
  • Цену закрытия с помощью функции iClose.
  • Вызов функции или метода print.
int OnInit()
  {
//--- Daily time frame
   CDailyPrices.timeFrame="Daily";
   CDailyPrices.open=(iOpen(Symbol(),PERIOD_D1,1));
   CDailyPrices.high=(iHigh(Symbol(),PERIOD_D1,1));
   CDailyPrices.low=(iLow(Symbol(),PERIOD_D1,1));
   CDailyPrices.close=(iClose(Symbol(),PERIOD_D1,1));
   CDailyPrices.pricesPrint();

//--- Weekly time frame
   CWeeklyPrices.timeFrame="Weekly";
   CWeeklyPrices.open=(iOpen(Symbol(),PERIOD_W1,1));
   CWeeklyPrices.high=(iHigh(Symbol(),PERIOD_W1,1));
   CWeeklyPrices.low=(iLow(Symbol(),PERIOD_W1,1));
   CWeeklyPrices.close=(iClose(Symbol(),PERIOD_W1,1));
   CWeeklyPrices.pricesPrint();

//--- Monthly time frame
   CMonthlyPrices.timeFrame="Monthly";
   CMonthlyPrices.open=(iOpen(Symbol(),PERIOD_MN1,1));
   CMonthlyPrices.high=(iHigh(Symbol(),PERIOD_MN1,1));
   CMonthlyPrices.low=(iLow(Symbol(),PERIOD_MN1,1));
   CMonthlyPrices.close=(iClose(Symbol(),PERIOD_MN1,1));
   CMonthlyPrices.pricesPrint();
   return(INIT_SUCCEEDED);
  }

После этого мы можем увидеть цены после работы советника на вкладке "Эксперты" панели инструментов:

 Цены

Здесь мы видим три строки:

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

indicatorClass:

Программа должна отображать значения четырех типов скользящих средних (простой, экспоненциальной, сглаженной и линейно-взвешенной), используя ООП. Ниже описаны простые шаги для достижения цели:

Объявим класс индикатора CiMA, используя ключевое слово class, и создадим общедоступные члены этого класса. Это четыре общие переменные: MAType - для определения типа скользящего среднего, MAArray - для определения массива скользящего среднего, MAHandle - для определения хендла каждого типа, MAValue - для определения значения каждой скользящей средней. Также создадим метод void или функцию valuePrint и тело функции для отображения значения каждого типа скользящего среднего.

class CiMA
  {
public:
   string            MAType;
   double            MAArray[];
   int               MAHandle;
   double            MAValue;
   void              valuePrint()
     {
      Print(MAType," Current Value: ",MAValue);
     };
  };

Создадим следующие объекты каждой скользящей средней из класса:

  • Имя средней
  • Хендл средней
  • Массив средней
//--- SMA
CiMA CSma;
CiMA CSmaHandle;
CiMA CSmaArray;

//--- EMA
CiMA CEma;
CiMA CEmaHandle;
CiMA CEmaArray;

//--- SMMA
CiMA CSmma;
CiMA CSmmaHandle;
CiMA CSmmaArray;

//--- LWMA
CiMA CLwma;
CiMA CLwmaHandle;
CiMA CLwmaArray;

Для каждого типа скользящей средней выполним следующие шаги внутри функции OnInit:

  • Определим имя средней.
  • Определим хендл средней.
  • Установим флаг AS_SERIES для массива с помощью ArraySetAsSeries.
  • Получим данные буфера средней с помощью функции CopyBuffer.
  • Определите значение средней и нормализуем его с помощью функции NormalizeDouble.
  • Вызовем созданный метод или функцию Print
int OnInit()
  {
   //--- SMA
   CSma.MAType="Simple MA";
   CSmaHandle.MAHandle=iMA(_Symbol,PERIOD_CURRENT,10,0,MODE_SMA,PRICE_CLOSE);
   ArraySetAsSeries(CSmaArray.MAArray,true);
   CopyBuffer(CSmaHandle.MAHandle,0,0,3,CSmaArray.MAArray);
   CSma.MAValue=NormalizeDouble(CSmaArray.MAArray[1],_Digits);
   CSma.valuePrint();

   //--- EMA
   CEma.MAType="Exponential MA";
   CEmaHandle.MAHandle=iMA(_Symbol,PERIOD_CURRENT,10,0,MODE_EMA,PRICE_CLOSE);
   ArraySetAsSeries(CEmaArray.MAArray,true);
   CopyBuffer(CEmaHandle.MAHandle,0,0,3,CEmaArray.MAArray);
   CEma.MAValue=NormalizeDouble(CEmaArray.MAArray[1],_Digits);
   CEma.valuePrint();

   //--- SMMA
   CSmma.MAType="Smoothed MA";
   CSmmaHandle.MAHandle=iMA(_Symbol,PERIOD_CURRENT,10,0,MODE_SMMA,PRICE_CLOSE);
   ArraySetAsSeries(CSmmaArray.MAArray,true);
   CopyBuffer(CSmmaHandle.MAHandle,0,0,3,CSmmaArray.MAArray);
   CSmma.MAValue=NormalizeDouble(CSmmaArray.MAArray[1],_Digits);
   CSmma.valuePrint();

   //--- LWMA
   CLwma.MAType="Linear-weighted MA";
   CLwmaHandle.MAHandle=iMA(_Symbol,PERIOD_CURRENT,10,0,MODE_LWMA,PRICE_CLOSE);
   ArraySetAsSeries(CLwmaArray.MAArray,true);
   CopyBuffer(CLwmaHandle.MAHandle,0,0,3,CLwmaArray.MAArray);
   CLwma.MAValue=NormalizeDouble(CLwmaArray.MAArray[1],_Digits);
   CLwma.valuePrint();
   return(INIT_SUCCEEDED);
  }

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

 Значения индикаторов

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


Заключение

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

Мы также узнали, как применять этот важный подход в MQL5, чтобы получить все эти невероятные возможности, а затем изучили несколько простых приложений, которые можно создать, применив ООП в MQL5.

Надеюсь, статья была полезна.

Если вы хотите узнать больше о создании в MQL5 торговых систем на основе наиболее популярных индикаторов, а также о разработке и применении пользовательских индикаторов в составе советников, вы можете ознакомиться с другими моими статьями. Вы можете найти полный список статей в моем профиле.

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/12813

Прикрепленные файлы |
indicatorClass.mq5 (2.09 KB)
priceClass.mq5 (1.7 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (6)
Alexey Viktorov
Alexey Viktorov | 22 сент. 2023 в 08:24
Alexey Volchanskiy #:

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

А теперь нас Мухамеды учат )). Статья перепечатка с какого-то занудного академического учебника, я уснул на первом абзаце. 

Наверное это было слишком рано. Тогда ещё мало кто использовал ООП в своей работе. А кто знал и использовал, тот не желал тратить время а обсуждение.

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

Valeriy Yastremskiy
Valeriy Yastremskiy | 22 сент. 2023 в 10:55

Начнем с определения ООП. ООП помогает создавать и разрабатывать повторно используемое программное обеспечение без дублирования работы и кода с применением концепции DRY (не повторяйся).

В этом что то есть, а где определение ООП?

Инкапсуляция это инкапсуляция которая позволяет... Заходило трудно, понятие видимость для ООПшников видимо приватно)))) И то что модификатор доступа это и есть инкапсуляция, об этом читатели сами должны догадаться))))

Да нормальный бизнес, за 200 тугриков выжимку из учебника делать, надеюсь хоть сам писал, без ГПТ))))

Viktor Vlasenko
Viktor Vlasenko | 25 сент. 2023 в 02:26

судя по:

"Внутри функции OnInit определим следующее для трех таймфреймов:

  • Таймфрейм строки   "

на качество статьи налагается качество перевода

Fedor Arkhipov
Fedor Arkhipov | 26 сент. 2023 в 21:24
Махмуд старался, а вы сразу накинулись :-)
Denis Kirichenko
Denis Kirichenko | 26 сент. 2023 в 22:39
Fedor Arkhipov #:
Махмуд старался, а вы сразу накинулись :-)
Так накинуться проще всего ))
Махмуд наверное знает поговорку "собака лает - караван идёт".
Разработка MQTT-клиента для MetaTrader 5: методология TDD Разработка MQTT-клиента для MetaTrader 5: методология TDD
Статья представляет собой первую попытку разработать нативный MQTT-клиент для MQL5. MQTT - это протокол обмена данными по принципу "издатель - подписчик". Он легкий, открытый, простой и разработан так, чтобы его было легко внедрить. Это позволяет применять его во многих ситуациях.
Язык визуального программиования ДРАКОН (Drakon) — средство общения для разработчика MQL и заказчика Язык визуального программиования ДРАКОН (Drakon) — средство общения для разработчика MQL и заказчика
ДРАКОН — язык визуального программирования, специально разработанный для упрощения взаимодействия между специалистами разных отраслей (биологами, физиками, инженерами...) с программистами в российских космических проектах (например, при создании создание комплекса "Буран"). В этой статье я расскажу о том, как ДРАКОН делает создание алгоритмов доступным и интуитивно понятным, даже если вы никогда не сталкивались с кодом, а также - как заказчику легче объяснить свои мысли при заказе торговых роботов, а программисту - совершать меньше ошибок в сложных функциях.
Как обнаруживать тренды и графические паттерны с помощью MQL5 Как обнаруживать тренды и графические паттерны с помощью MQL5
В статье представлен метод автоматического обнаружения моделей ценовых действий с помощью MQL5, таких как тренды (восходящий, нисходящий, боковой) и графические модели (двойная вершина, двойное дно).
Готовые шаблоны для подключения индикаторов в экспертах (Часть 2): Индикакторы объёма и Билла Вильямса Готовые шаблоны для подключения индикаторов в экспертах (Часть 2): Индикакторы объёма и Билла Вильямса
В статье рассмотрим стандартные индикаторы из категории Объемов и индикаторов Билла Вильямса. Создадим готовые к применению шаблоны использования индикаторов в советниках — объявление и установка параметров, инициализация, деинициализация индикаторов и получение данных и сигналов из индикаторных буферов в советниках.