Обсуждение статьи "Реализация модели таблицы в MQL5: Применение концепции MVC"

 

Опубликована статья Реализация модели таблицы в MQL5: Применение концепции MVC:

В статье рассмотрим процесс разработки модели таблицы на языке MQL5 с использованием архитектурной концепции MVC (Model-View-Controller) для разделения логики данных, представления и управления, что помогает создавать структурированный, гибкий и масштабируемый код. Рассмотрим реализацию классов для построения модели таблицы, включая использование связанных списков для хранения данных.

В программировании архитектура приложения играет ключевую роль в обеспечении надёжности, масштабируемости и удобстве поддержки. Одним из подходов, который помогает достичь таких целей, является использование архитектурного шаблона MVC (Model-View-Controller).

Концепция MVC позволяет разделить приложение на три взаимосвязанных компонента: модель (управление данными и логикой), представление (отображение данных) и контроллер (обработка действий пользователя). Такое разделение упрощает разработку, тестирование и поддержку кода, делая его более структурированным и гибким.

В данной статье мы рассмотрим, как применить принципы MVC для реализации модели таблицы на языке MQL5. Таблицы являются важным инструментом для хранения, обработки и отображения данных, и их правильная организация может значительно упростить работу с информацией. Мы создадим классы для работы с таблицами: ячейки таблицы, строки и модель таблицы. Для хранения ячеек в строках и строк в модели таблицы будем использовать связанные списки Стандартной Библиотеки MQL5, которые позволяют эффективно хранить и использовать данные.

Автор: Artyom Trishkin

 

Привет Артём.

Вот такой вопрос:

В твоём коде читается тип объекта.

      //--- Читаем тип объекта
      this.m_element_type=(ENUM_OBJECT_TYPE)::FileReadInteger(file_handle,INT_VALUE);

Но не проверяется как в СБ

bool CList::Load(const int file_handle)
  {
   uint     i,num;
   CObject *node;
   bool     result=true;
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//--- read and checking begin marker - 0xFFFFFFFFFFFFFFFF
   if(FileReadLong(file_handle)!=-1)
      return(false);
//--- read and checking type
   if(FileReadInteger(file_handle,INT_VALUE)!=Type())
      return(false);
//--- read list size
   num=FileReadInteger(file_handle,INT_VALUE);
//--- sequential creation of list items using the call of method Load()
   Clear();
   for(i=0;i<num;i++)
     {
      node=CreateElement();
      if(node==NULL)
         return(false);
      Add(node);
      result&=node.Load(file_handle);
     }
//--- successful
   return(result);
  }

Как видим метод Type() просто возвращает значение

   virtual int       Type(void) const { return(0x7779); }

А вообще какова необходимость такой проверки в СБ? Может действительно достаточно прочесть тип и создать элемент соответствующего типа?

 
Alexey Viktorov #:
this.m_element_type=(ENUM_OBJECT_TYPE)::FileReadInteger(file_handle,INT_VALUE);

Ну, если тип прочитан не будет, то что далее?

Вот код:

//--- Последовательно заново создаём элементы списка с помощью вызова метода Load() объектов node
   this.Clear();
   for(uint i=0; i<num; i++)
     {
      //--- Читаем и проверяем маркер начала данных объекта - 0xFFFFFFFFFFFFFFFF
      if(::FileReadLong(file_handle)!=MARKER_START_DATA)
         return false;
      //--- Читаем тип объекта
      this.m_element_type=(ENUM_OBJECT_TYPE)::FileReadInteger(file_handle,INT_VALUE);
      node=this.CreateElement();
      if(node==NULL)
         return false;
      this.Add(node);
      //--- Сейчас файловый указатель смещён относительно начала маркера объекта на 12 байт (8 - маркер, 4 - тип)
      //--- Поставим указатель на начало данных объекта и загрузим свойства объекта из файла методом Load() элемента node.
      if(!::FileSeek(file_handle,-12,SEEK_CUR))
         return false;
      result &=node.Load(file_handle);
     }
//--- Результат
   return result;
  }
//+------------------------------------------------------------------+
//| Метод создания элемента списка                                   |
//+------------------------------------------------------------------+
CObject *CListObj::CreateElement(void)
  {
//--- В зависимости от типа объекта в m_element_type, создаём новый объект
   switch(this.m_element_type)
     {
      case OBJECT_TYPE_TABLE_CELL   :  return new CTableCell();
      case OBJECT_TYPE_TABLE_ROW    :  return new CTableRow();
      case OBJECT_TYPE_TABLE_MODEL  :  return new CTableModel();
      default                       :  return NULL;
     }
  }

Читаем тип объекта в переменную. Далее пытаемся создать этот объект в CreateElement(), а там - кейсы. Что вернёт этот метод, если не будет прочитан из файла тип создаваемого объекта?

 
Artyom Trishkin #:

Ну, если тип прочитан не будет, то что далее?

Вот код:

Читаем тип объекта в переменную. Далее пытаемся создать этот объект в CreateElement(), а там - кейсы. Что вернёт этот метод, если не будет прочитан из файла тип создаваемого объекта?

Артём, я не об этом. Я говорю о том, что в СБ есть проверка типа. Именно проверка.

//--- read and checking type
   if(FileReadInteger(file_handle,INT_VALUE)!=Type())
      return(false);

Соответствует ли тип прочитанный из файла, типу из метода Type() Чтение и проверка типа. Так переводится?
А у тебя просто чтение типа без проверки. 

Вот и вопрос: А какой такой глубокий смысл заложен в эту проверку?

 
Alexey Viktorov #:
Вот и вопрос: А какой такой глубокий смысл заложен в эту проверку?

Когда класс объекта SomeObject загружается из файла, вызвав метод Load() этого самого объекта SomeObject, то он проверяет, "а правда ли я себя прочитал из файла?" (ты именно об этом спрашиваешь). Если нет, то значит что-то пошло не так, соответственно, и загружать нет смысла дальше.

У меня же тут СПИСОК (CListObj) читает тип объекта из файла. Список не знает что там (какой объект) лежит в файле. Но он должен знать этот тип объекта - чтобы создать его в своём методе CreateElement(). Поэтому тут и не проверяется тип загруженного объекта из файла. Ведь будет сравнение с типом Type(), который в данном методе возвращает тип списка, а не объекта.

 
Artyom Trishkin #:

Когда класс объекта SomeObject загружается из файла, вызвав метод Load() этого самого объекта SomeObject, то он проверяет, "а правда ли я себя прочитал из файла?" (ты именно об этом спрашиваешь). Если нет, то значит что-то пошло не так, соответственно, и загружать нет смысла дальше.

У меня же тут СПИСОК (CListObj) читает тип объекта из файла. Список не знает что там (какой объект) лежит в файле. Но он должен знать этот тип объекта - чтобы создать его в своём методе CreateElement(). Поэтому тут и не проверяется тип загруженного объекта из файла. Ведь будет сравнение с типом Type(), который в данном методе возвращает тип списка, а не объекта.

Спасибо, разобрался, понял.

 

прочитал..потом ещё раз перепрочёл

это всё что угодно кроме кроме "модели" в MVC. Некий ListStorage например

 
Давайте по делу. Свое мнение оставьте при себе.
 
Интересно. Можно ли таким образом получить некий аналог датафреймов питона и R? Это такие таблицы, где в разных столбцах могут быть данные разных типов (из ограниченного набора типов, но включая string).
 
Aleksey Nikolayev #:
Интересно. Можно ли таким образом получить некий аналог датафреймов питона и R? Это такие таблицы, где в разных столбцах могут быть данные разных типов (из ограниченного набора типов, но включая string).

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