Discussing the article: "Implementation of a table model in MQL5: Applying the MVC concept"

 

Check out the new article: Implementation of a table model in MQL5: Applying the MVC concept.

In this article, we look at the process of developing a table model in MQL5 using the MVC (Model-View-Controller) architectural pattern to separate data logic, presentation, and control, enabling structured, flexible, and scalable code. We consider implementation of classes for building a table model, including the use of linked lists for storing data.

In programming, application architecture plays a key role in ensuring reliability, scalability, and ease of support. One of the approaches that helps achieve such goals is to leverage architecture pattern called MVC (Model-View-Controller).

MVC concept allows you to divide an application into three interrelated components: model (data and logic management), view (data display), and controller (processing user actions). This separation simplifies code development, testing, and maintenance, making it more structured and flexible.

In this article, we consider how to apply MVC principles to implement a table model in the MQL5 language. Tables are an important tool for storing, processing, and displaying data, and properly organizing them can make working with information much easier. We will create classes for working with tables: table cells, rows, and a table model. To store cells within rows and rows within the table model, we will use the linked list classes from the MQL5 Standard Library that allow efficient storage and use of data.


Author: Artyom Trishkin

 

Hi, Artem.

Here's a question:

Your code reads the object type.

      //--- Read object type
      this.m_element_type=(ENUM_OBJECT_TYPE)::FileReadInteger(file_handle,INT_VALUE);

But it is not checked as in SB

bool CList::Load(const int file_handle)
  {
   uint     i,num;
   CObject *node;
   bool     result=true;
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//--- reading and checking begin marker - 0xFFFFFFFFFFFFFFFFFFFFF
   if(FileReadLong(file_handle)!=-1)
      return(false);
//--- reading 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);
  }

As we can see, the Type() method simply returns a value

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

What is the necessity of such a check in SB? Is it really enough to read the type and create an element of the corresponding type?

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

Well, if the type is not read, what next?

Here is the code:

//--- Sequentially recreate the list items by calling the Load() method of node objects
   this.Clear();
   for(uint i=0; i<num; i++)
     {
      //--- Read and check the object data start marker - 0xFFFFFFFFFFFFFFFFFFFFFFF
      if(::FileReadLong(file_handle)!=MARKER_START_DATA)
         return false;
      //--- Read object type
      this.m_element_type=(ENUM_OBJECT_TYPE)::FileReadInteger(file_handle,INT_VALUE);
      node=this.CreateElement();
      if(node==NULL)
         return false;
      this.Add(node);
      //--- Now the file pointer is offset relative to the beginning of the object marker by 12 bytes (8 - marker, 4 - type).
      //--- Let's put a pointer to the beginning of the object data and load the object properties from the file using the Load() method of the node element.
      if(!::FileSeek(file_handle,-12,SEEK_CUR))
         return false;
      result &=node.Load(file_handle);
     }
//--- Result
   return result;
  }
//+------------------------------------------------------------------+
//| Method for creating a list item|
//+------------------------------------------------------------------+
CObject *CListObj::CreateElement(void)
  {
//--- Depending on the object type in m_element_type, create a new object
   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;
     }
  }

Read the type of the object into a variable. Then we try to create this object in CreateElement(), and there are cases. What will this method return if the type of the object being created is not read from the file?

 
Artyom Trishkin #:

Well, if the type is not read, then what next?

Here's the code:

We read the type of the object into a variable. Then we try to create this object in CreateElement(), and there are cases. What will this method return if the type of the object being created is not read from the file?

Artem, that's not what I'm talking about. I'm talking about the fact that there is a type check in SB. Exactly the check.

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

Does the type read from the file match the type from the Type()Read and typecheck method. Is that how it translates?
And you just read the type without checking it.

So the question is: What is the deep meaning of this check?

 
Alexey Viktorov #:
Here's the question: What is the deep meaning of this check?

When a class of SomeObject is loaded from a file by calling the Load() method of this very SomeObject, it checks "did I really read myself from the file?" (that's what you're asking). If not, it means something went wrong, so there's no point in loading any further.

What I have here is a LIST (CListObj) reading an object type from a file. The list does not know what is there (what object) in the file. But it must know this object type to create it in its CreateElement() method. That's why it doesn't check the type of the loaded object from the file. After all, there will be a comparison with Type(), which in this method returns the type of a list, not an object.

 
Artyom Trishkin #:

When a class of SomeObject is loaded from a file by calling the Load() method of this very SomeObject, it checks "did I really read myself from the file?" (that's what you're asking). If not, it means that something went wrong, so there is no point in loading further.

What I have here is a LIST (CListObj) reading an object type from a file. The list does not know what is there (what object) in the file. But it must know this object type to create it in its CreateElement() method. That's why it doesn't check the type of the loaded object from the file. After all, there will be a comparison with Type(), which in this method returns the type of a list, not an object.

Thanks, I've figured it out, I understand.

 

I read it, and then I reread it again.

it's anything but a "model" in MVC. Some ListStorage for example

 
Let's get to the point. Keep your opinions to yourself.
 
I wonder. Is it possible to get some analogue of python and R dataframes in this way? These are tables where different columns can contain data of different types (from a limited set of types, but including string).
 
Aleksey Nikolayev #:
I wonder. Is it possible to get some analogue of python and R dataframes in this way? These are such tables where different columns can contain data of different types (from a limited set of types, but including string).

You can. If we are talking about different columns of one table, then in the described implementation each cell of the table can have a different data type.