Вопросы по ООП (Объектно Ориентированному Программированию ) - страница 6

 
C-4:
Указатели не заменимы при сложных преобразованиях объектов, когда необходима динамическая идентификация типов.

Василий, пример, пожалуйста!

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

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

 
Integer:


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


Именно. И как раз тут важно правильно спроектировать класс. А для этого важно спрогнозировать какие будут следующие классы (наследники).

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

В случае с MQL - наверно наоборот - побольше функционала в базовом классе, как у VOLDEMAR'a. Но без фанатизма.


Есть свойства торгового символа (POINT, STOPLEVEL и пр. MODE_ХХХ для MarketInfo() ). Их лучше запихать в класс cSymbol, например.


Есть свойства ордера (цена открытия, тип, лот, и пр. OrderXXX() ). Их лучше запихать в отдельный класс cOrder, например.

Если мы вспомним, что ордер можно закрывать частями, то ещё зададим классу поле BaseLot (чтобы узнать какая часть лота уже закрыта) и пр.


Есть (для меня) расписание - заданные периоды времени (по дням) когда можно открывать и закрывать ордера, периоды когда только закрываем (тралим если уже в безубытке), и период когда совсем не работаем и тогда закрываем всё открытое и удаляем неоткрытое.

Все же помнят про гэпы и что в последний час торгов во многих (если не во всех) ДЦ меняются условия торговли и что во многих ДЦ есть перерыв в торговле металлами каждый торговый день ?


И, наконец, есть советник - определенный алгоритм, который работает с определенным символом, по определенному расписанию, с определенным списком "своих" ордеров и со своими данными для расчета.

Можно создать класс cExpert, в котором будет объект класса cSymbol и массив объектов класса cOrder. В этом базовом cExpert будут функции обновления свойств объектов cOrder, функции по работе с ордерами, обработка ошибок, статистика и пр.

Лично мне полезны функции расчета лота в %% от AccountFreeMargin(), генерация уникального магика для каждого ордера (проще поставить реверсивный ордер к конкретному ордеру), функция выставления реверсивного ордера и пр.

И вот уже от этого cExpert можно делать наследников с уникальными наборами дополнительных данных и функций, которые и будут решать когда-что делать на рынке (открывать/сопровождать/закрывать ордера).

Т.е. реализовывать торговые стратегии. И являться тем, что мы и называем "советник по стратегии XXX".


Но ВЕСЬ базовый функционал будет зашит в базовом классе cExpert. В классах-потомках уже будут алгоритмы торговых стратегий.

Ну, может быть ещё кто-то добавит расширенную обработку ошибок или расширенную статистику по торговле (если не будет исходников и не смогут запихать в базовый cExpert).

Обратите внимание, что мы тратим время и делаем класс, который упростит написание советников. Фактически - шаблон советника. Структурно законченный и неразмазанный (все нужные данные - в одном месте с кодом).


Что касается якобы "излишества" функций-оберток над openorders(), то лично я как раз поддерживаю такие обертки (особенно - если в них есть доп функционал, а не просто вызов базовой функции).

Например, если в вызове оберток SL и TP задан в пунктах и надо пересчитать в абсолютные цифры, да еще прибавить/отнять в зависимости от типа ордера (хотя и в этом случае такие преобразования можно в operorders() запихать).

Лично мне - проще понять код с вызовом BuyStop(...) и один раз проверить что BuyStop() всё делает правильно, чем разбирать каждый раз параметры OrderSend() на предмет корректности параметров.

PS: Было много работы, в т.ч. на выходных. Спасибо Pavlick и mql5 за коды-примеры.

Изучите эти примеры и подумайте - надо ли городить иерархию классов в области программирования задач, которые пишутся на MQL?

Действительно ли вам нужно целое семейство классов на основе базового (все эти "треугольники", "квадраты" и пр.)

И ещё раз золотые слова Integer'a: Не надо лезть в дебри только ради того, чтобы быть в дебрях.

О. то ли я не заметил, то ли недавно появился раздел документации с примерами.

 
Zhunko:

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

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


Тут речь, видимо, вот о чем.


Допустим есть класс cFather, у него есть метод int GetData(), который возвращает 3. И метод PrintData(), которая выводит то, что получит от GetData().

Есть его потомок cChild, у которого перекрыт GetData(), который теперь возвращает 5.

Если мы объявим объект типа cFather TestObject, то TestObject.GetData() всегда будет выдавать 3.

Если мы объявим cFather* TestObject=new cChild1, то TestObject.GetData() будет выдавать 5, несмотря на то, что он вроде бы cFather.

Нужно для того, чтобы код, написанный сейчас, мог вызвать метод GetData() у любого наследника класса сFather, даже если сейчас его (класса-наследника) ещё не существует.

Т.е. если потом появится класс cChild2, у которого GetData() будет возвращать 7, то после cFather* Test2=new cChild2 функция Test2.PrintData() начнет выводить 7.

Если есть какая-то функция, которая ожидает параметр "ссылка на объект класса cFather" и использует его GetData(), то она будет получать корректные данные для любого потомка сFather.

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

См. тут и тут

 
EverAlex:

Тут речь, видимо, вот о чем.

...

Есть такой оператор "::", который позволяет обратится к любому методу во всей цепочке базовых и в производном классах без выделения памяти и указателя.
 
C-4:


У вас класс на 90% избыточен. Основную работу выполняют только две функции, это openorders и tip Зачем Вы используйте Sel, Buy SelStop и т.д., если фактически все они лишь вызывают Openorders? Далее тип ордера передается как int а значит не защищен. Вместо int лучше использовать либо свое перечисление, либо стандартное ENUM_ORDER_TYPE. И вообще лучше никогда не исопльзуйте магические цифры "1", "2" и т.д., только перечисления. Это не даст заслать в функцию левое значение ордера. Cама функция Openorders слишком большая. Очевидно что она состоит из двух блоков, блока заключения сделки и блока проверки условий. Каждый из них должен быть в виде отдельной приватной функции.

Для начала не плохо, но еще многому надо учиться. Функцию tip лучше переписать так:

Мне удобно при вызове метода я визуально вижу какой тип ордера выставляю ...

Насчет сравнения опишите детально почему так не рекомендуется сравнивать дабл с 0 ?

 
C-4:
Указатели не заменимы при сложных преобразованиях объектов, когда необходима динамическая идентификация типов.

Наличие динамической идентификации типов обычно говорит о костыльной архитектуре проекта.
 
если класс объявлен на глобальном уровне в советнике (Class c;) будут ли сохраняться состояния внутренних объектов класса измененных на одном тике по приходу последующего тика ?
 
EverAlex:

Тут речь, видимо, вот о чем.


Допустим есть класс cFather, у него есть метод int GetData(), который возвращает 3. И метод PrintData(), которая выводит то, что получит от GetData().

Есть его потомок cChild, у которого перекрыт GetData(), который теперь возвращает 5.

Если мы объявим объект типа cFather TestObject, то TestObject.GetData() всегда будет выдавать 3.

Если мы объявим cFather* TestObject=new cChild1, то TestObject.GetData() будет выдавать 5, несмотря на то, что он вроде бы cFather.

Нужно для того, чтобы код, написанный сейчас, мог вызвать метод GetData() у любого наследника класса сFather, даже если сейчас его (класса-наследника) ещё не существует.

Т.е. если потом появится класс cChild2, у которого GetData() будет возвращать 7, то после cFather* Test2=new cChild2 функция Test2.PrintData() начнет выводить 7.

Если есть какая-то функция, которая ожидает параметр "ссылка на объект класса cFather" и использует его GetData(), то она будет получать корректные данные для любого потомка сFather.

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

См. тут и тут

class cFather
{
public:
    int GetData() {return 3;}
};

class cChild : public cFather
{
public:
    int GetData() {return 5;}
};
    
int f(cFather *p) {return p->GetData();}
    
int main()
{
    cChild obj;
    f(&obj);                // вернет 3
    obj.cFather::GetData(); // вернет 3
    
    return 0;
}
 
Pavlick:


Наверное зря я пример написал. В МКЛ это не прокатывает:

cChild obj;
f(&obj);                // вернет 3
obj.cFather::GetData(); // вернет 3

Х.з, не указатели, а смех на палке.

P.S: пишите в dll'ях, есть шанс освоить нормальный язык.

 
Pavlick:


Наверное зря я пример написал. В МКЛ это не прокатывает:

Х.з, не указатели, а смех на палке.

P.S: пишите в dll'ях, есть шанс освоить нормальный язык.


class cFather
  {
public:
   int GetData() {return 3;}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class cChild : public cFather
  {
public:
   int GetData() {return 5;}
  };

int f(cFather *p) {return p.GetData();}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnStart()
  {
   cChild obj,*ptr=GetPointer(obj);
   f(ptr);                     // вернет 3
   ((cFather *)ptr).GetData(); // вернет 3

   return 0;
  }
Причина обращения: