The words of truth are simple

Euripides

The concept of object-oriented programming is both simple and complex. Its simplicity lies in the conciseness of its solutions: the hierarchical structure of the description of a set of entities with their properties, connections and possibilities of interaction with other entities practically eliminates the duplication of terms and their interpretations. This article is devoted to an attempt to create a convenient and efficient system for organizing knowledge in the field of programming, which can be useful not only in working with My Visual Database (MVDB), but also with other systems using an object-oriented approach (OOP). ).

A digression

I have been thinking for a long time why MVDB has such a poorly developed system of documentation and descriptions of the classes that are available in it, and I came to the conclusion that it is difficult to choose a form for presenting such information. 

A hundred years ago, there were practically no alternatives to a one-dimensional information transmission channel. Books, of course, still fulfill their functions of transferring knowledge, but extracting them requires a consistent reading of its structural units, and the table of contents is the only way to capture the essence of the material presented at a glance.

Of course, there is still painting – paintings by great masters are able to convey concepts in two-dimensional representation, and detailed study allows you to “scale” information when reading, approaching or moving away from the picture. But the language of fine arts is more focused on spiritual and aesthetic enlightenment. And although people have come up with drawings to present technical problems, working with them is difficult and their possibilities are also limited.

With the advent of computers, new possibilities appeared: hypertext, multimedia, interactivity. The two-dimensionality of the transmission channel has become full-fledged, attempts continue to expand it both through the simultaneous use of other sensory channels (sound, tactile sensations), and by adding three-dimensionality to the created images, as well as experiments in the field of augmented reality. 

But I suppose that further expansion of the channel will require a change in the internal structure of a person, his psyche, his sensory and cognitive abilities. Alas, there are no objective prerequisites based on the real needs of mankind for this: despite the successes of technological progress, humanity first of all needs to solve social and environmental issues, which for the most part lie in the personal plane

But still, let me introduce a system for accumulating special knowledge in the field of object-oriented approach, which has been successfully implemented in many programming languages ​​and development environments, including MVDB.

Basic and superstructure

These concepts of Marxist theory can be applied in our case, but with the amendment that the add-on (development environment MVDB) in our case does not affect the basis (RAD Delphi and language ObjectPascal).

According to unofficial information, MVDB is compiled in Delphi XE3 using both standard components included in Delphi and a number of additional ones that provide reporting, displaying tabular data and Google map data. Despite the rather modest component panel of the MVDB form editor, there are still about 170 (!) classes “on board the airliner”, including dozens of classes of visual components that can be used in applications created using scripts. I remembered the old joke.

Landing on the superliner IL-2086 has ended. The stewardess enters the cabin:
"Ladies and gentlemen, in order to help you pass the time of the flight, on board our liner there is a library, a cinema hall, three bars, a restaurant, a swimming pool and two tennis courts. And now I will ask you to fasten your seat belts, because now, along with all this bullshit we'll try to take off!"

The size of the executable module of the application program created by MVDB is 20,092,928 bytes (version 6.6 beta) and does not depend on the complexity of the project, the visual and non-visual components used. This is the price of portability: all the libraries that may be required for work are located inside the executable module. The project itself is represented by a user interface forms description file (forms.xml), a compiled scripts file (script.dcu) and other auxiliary files necessary for connecting to the database, creating reports and displaying graphical content.

Conceptually, MVDB allows you to create applications for working with databases without writing code, since all the main functions for this are implemented in the main visual components. That is, development comes down to creating a data schema (again using a visual designer), placing visual components on forms and setting their key properties. To learn how to use the program in this mode, there is enough information presented both in the form of a written manual and in the form of videos.

However, to use the full power of the built-in programming language and available classes, you will need a good knowledge of OOP principles and the Delphi programming language and development environment. The main problem is that this environment (base) continues to evolve, unlike the add-on, the development of which stalled about a year ago, and the information presented on the official Embarcadero websites reflects the modern development of the class hierarchy and components available in the latest version of Delphi. Therefore, some of the information about the components and classes that are hidden under the hood of MVDB now needs to be looked for in the archives of software development sites, various forums and blogs. 

The question of creating reports stands apart, since FastReport is still sold as a separate subsystem and used in various programs. The project has its own documentation, including in Russian. But she has the same problem as described above – a linear flow of material, which is quite difficult in places.

Class Explorer

The developed information system will consist of two main blocks: navigation and display.

Navigation

The purpose of the navigation system is to organize the search for the necessary information and transfer information about the structure of relationships and dependencies between the individual described entities. Navigation is based on the structure of the stored information.

Search

The search will be through all the entities stored in the help system: classes, types, methods, properties, events, and so on. The result will be displayed in a list with a short description, when clicked, a detailed description will be displayed (example of use).

Display

For display purposes, the TdbRichEdit component is a text editor with rich options for adding data in various presentation formats (images, tables, etc.)

Data Schema

Tables with core entities have name (name) and description (short description) fields, as well as a link to the example table, which stores detailed descriptions and usage examples. Such a scheme is convenient, since some properties and methods will be repeated in different classes and there is no need to make a separate detailed description for each of them. At the same time, sometimes you want to make a specific example for a separate class. The proposed scheme allows a flexible approach to the formation of reference content and its volume.

Because My Visual Database does not clearly support script-level inheritance (that is, the presence of a property or method in a base class does not mean that it is available in scripts of both the class itself and child classes), then there will be no mechanisms for storing and receiving inherited methods, properties and events in this program.

Classes and types

A class is an evolutionary extension of a type, so it makes sense to use the same classType table to store them all. But for the convenience of filtering the data, I added a logical field isType, in which I will explicitly indicate whether this entry belongs to types. This is useful when rendering a list of types and a list of classes. But for the class tree, the parentID field is required.

Functions and Procedures

Procedures, functions, and class methods are stored in the funcProc table. To understand which class this method belongs to, use the reference field id_classType. If it is empty, then this entry refers to public functions and procedures available in MVDB scripts. This fact is used when filtering data to display a list of class methods and a list of functions. The second reference field id_classType1 is required to indicate the return type of the function result. If it is empty, then we are dealing with a procedure (for the convenience of displaying the function result type, a calculated field ResultType has been added). But to display the function tree (to divide functions into categories), two more fields were required: parentID to store a link to the parent element and isGroup to store the sign that the element is the name of a group (the group is displayed in the tree, but not in the list of functions).

Properties and global variables

The property table is designed to store these entities. For properties, it is necessary to fill in the reference field id_classType, if it is empty, then the record stores information about the global variable (there are only three of them, but do not forget about them). The id_classType1 field is used to specify the property type (for the convenience of displaying the property type, the calculated field ResultType has been added).

Events

Class events for which you can create a handler are stored in the classEvent table. To communicate with the class, the id_classType field is used, and in the id_funcProc field, you can store a link to the procedure for handling this event. More precisely, not for a specific procedure, but for a procedure that suits the composition and number of parameters – this is the main criterion for custom event handler procedures.

When creating a handler procedure from the visual development environment, just click on the field with the name of the event, so that the system itself generates a template for such a procedure. However, one should not forget that in some cases it is necessary to create such procedures on your own, not forgetting about the composition of the parameters.

Parameters of procedures and functions

The funcProcParam table is used to store the list of parameters. It would be possible to do without it, specifying the parameters in the full description of the function description, but in order to complete the structural presentation, I decided to add a separate table. In addition to the connection with the id_funcProc function, the orderNum field stores the ordinal number of the parameter.

Named constants

In fact, most types are integers, but to increase the reliability of writing code in the Pascal language, strong typing is used, and for the convenience of working with type values, named constants are used. To store them, the program provides a table typeConst.

Tasks

In practice, the developer solves specific tasks related to displaying or searching for data. That is why we need a task table, where typical tasks will be stored in a hierarchical form, which usually answer the question “how”: how to add an item to the main menu, how to color the table in different colors, how to automatically make backup database copies and so on. To create a hierarchical structure, you need the parentID field, and in order to distinguish the categories from the tasks themselves, the isGroup field

Examples

The same example can be used to illustrate different classes, procedures, or tasks. All of them are stored in the example table, which has only two fields: caption (title) and detail (content). This field will store the data generated by the TdbRichEdit component, which will allow you to include formatted source texts or images in examples.

Screen Forms

The application will have a main form and forms for editing directories.

The structure of the main form turns out to be quite complex, so I decided to use the technique of dynamic assembly of the main form: the main form has multi-page tabs, as well as a panel with edit buttons and a search bar. And table and tree views are on separate forms that are connected to the main form at the time of application launch (in the future I plan to create them using scripts, abandoning the visual editor – they are simple and of the same type). For this I will use the Form_ShowOnWinControl() procedure.

Scripts

For convenience, you will need to add splitters – elements with which you can resize visual components in a running application. I will not need the standard main menu, so I need to hide it. For all this, the procedure InitForm is useful.

procedure InitForm;
var
  tmpSplitter: TSplitter;
begin
  // remove main menu
  frmMain.Menu := nil;
  frmMain.DoubleBuffered := True;
  // add splitters and alignment
  // classes
  frmMain.pgcClassView.align := alLeft;
  tmpSplitter := TSplitter.Create(frmMain);
  tmpSplitter.Parent := frmMain.tshClass;
  tmpSplitter.Left := frmMain.pgcClassView.Width + 1;
  tmpSplitter.Width := 7;
  tmpSplitter.align := alLeft;
  frmMain.pgcClass.align := alClient;
  // types
  frmMain.pgcTypeView.align := alLeft;
  tmpSplitter := TSplitter.Create(frmMain);
  tmpSplitter.Parent := frmMain.tshType;
  tmpSplitter.Left := frmMain.pgcTypeView.Width + 1;
  tmpSplitter.Width := 7;
  tmpSplitter.align := alLeft;
  frmMain.pgcType.align := alClient;
  // functions
  frmMain.pgcFunctionView.align := alLeft;
  tmpSplitter := TSplitter.Create(frmMain);
  tmpSplitter.Parent := frmMain.tshFunction;
  tmpSplitter.Left := frmMain.pgcFunctionView.Width + 1;
  tmpSplitter.Width := 7;
  tmpSplitter.align := alLeft;
  frmMain.pgcFunction.align := alClient;
  // variables
  frmMain.pgcVariableView.align := alClient;
  // tasks
  frmMain.pgcTaskView.align := alClient;
  // methods
  frmMain.panMethod.align := alTop;
  tmpSplitter := TSplitter.Create(frmMain);
  tmpSplitter.Parent := frmMain.tshMethod;
  tmpSplitter.Top := frmMain.panMethod.Height + 1;
  tmpSplitter.Height := 7;
  tmpSplitter.align := alTop;
  frmMain.pgcMethodParamView.align := alClient;
 
  Form_ShowOnWinControl( frmClassTree, frmMain.tshClassTree );
  Form_ShowOnWinControl( frmClassList, frmMain.tshClassList );
  Form_ShowOnWinControl( frmTypeList, frmMain.tshTypeList);
  Form_ShowOnWinControl( frmFunctionTree, frmMain.tshFunctionTree );
  Form_ShowOnWinControl( frmFunctionList, frmMain.tshFunctionList );
  Form_ShowOnWinControl( frmVariableList, frmMain.tshVariableList );
  Form_ShowOnWinControl( frmTaskTree, frmMain.tshTaskTree );
  Form_ShowOnWinControl( frmTaskList, frmMain.tshTaskList );
  Form_ShowOnWinControl( frmProperty, frmMain.tshProperty );
  Form_ShowOnWinControl( frmMethod, frmMain.panMethod );
  Form_ShowOnWinControl( frmEvent, frmMain.tshEvent );
  Form_ShowOnWinControl( frmMethodParamList, frmMain.tshMethodParamList );
  Form_ShowOnWinControl( frmTypeConst, frmMain.tshTypeConst );
  Form_ShowOnWinControl( frmFunctionParam, frmMain.tshFunctionParam );
  Form_ShowOnWinControl( frmSearchResult, frmMain.tshSearchResult );
  Form_ShowOnWinControl( frmExampleView, frmMain.tshExample );
  //
  Tree_LoadCollapseList( frmFunctionTree.trvMain );
  Tree_LoadCollapseList( frmClassTree.trvMain );
  Tree_LoadCollapseList( frmTaskTree.trvMain );
end;Code language: Delphi (delphi)

The Form_ShowOnWinControl() procedure “attaches” the form to any descendant of TWinControl, in our case it will be the multipage and panel tabs. It also calls the universal procedure Form_UpdateData() to update the data on the form.

procedure Form_ShowOnWinControl(AForm: TAForm; AControl: TWinControl;);
// displaying a form on a container
begin
  // fasten the uniform if it is not already fastened
  if AForm.Parent = nil then
  begin
    // preparing the form
    AForm.borderStyle := bsNone;
    AForm.Align := alClient;
    // put it on the panel
    AForm.Parent := AControl;
    SetParent(AForm.Handle, AControl.Handle);
  end;
  // make the form visible
  AForm.Visible := True;
  // update form data
  Form_UpdateData(AForm);
  AForm.BringToFront;
end;Code language: Delphi (delphi)

Updating data is based on the principles of naming conventions, according to which a form can have a button for updating data called btnUpdate. If it is not present, then the tgrMain table view or the trvMain tree is directly updated, and the tree sorting is corrected for the elements by setting the dbCustomOrderBy property. This will arrange the nodes of the tree within the branch alphabetically.

procedure Form_UpdateData(AForm: TAForm);
// update table data on the form
var
  tmpButton: TdbButton;
  tmpGrid: TdbStringGridEx;
  tmpTree: TdbTreeView;
begin
  FindC(AForm, 'btnUpdate', tmpButton, False);
  if tmpButton <> nil then
    tmpButton.Click
  else
  begin
    FindC(AForm, 'tgrMain', tmpGrid, False);
    if tmpGrid <> nil then
    begin
      tmpGrid.dbUpdate;
    end
    else
    begin
      FindC(AForm, 'trvMain', tmpTree, False);
      if tmpTree <> nil then
      begin
        // add secondary tree sorting by name
        tmpTree.dbCustomOrderBy := 'ParentID,name';
        tmpTree.dbUpdate; //
      end
    end
  end;
end;Code language: PHP (php)

To be continued

Leave a Reply

Your email address will not be published. Required fields are marked *