Sunday, March 25, 2018

Part 1 of 4—Overview—Using Delphi LiveBindings With TObject and TObjectList

Source Code

The source code in the form of a Delphi Project Group is available at:
The project was developed using Delphi RAD Studio Tokyo (10.2.2)

The README file contains additional useful information.

Overview

Need

Working with another project, I had a need to import a JSON object with a number of properties, one of which was an array of JSON objects. This lends itself to a hierarchy of Objects and ObjectLists, so I set about constructing the needed data objects and parsing my input JSON (that comes from a file) to populate the objects I had created.

I also wanted to display the contents of the data thus imported using TListView components in a FireMonkey application using LiveBindings.

I realized that I didn't have much understanding about all of this so I set about learning a thing or three. What I share with you now are the results of those explorations.

Demo Project

The Demo Project is named LiveBindingObjects.exe and when run, produces the following UI (a full-sized .png of this screen shot is also to be found in the Docs folder of the Github project or downloaded here):

LiveBindingObjects.exe User Interface Screen Shot
LiveBindingObjects.exe User Interface

Launching the Program

After cloning or downloading and unzipping the source project group (if necessary) make the project LiveBindingObjects.exe the active project. Choose the Target Platform if necessary. Note that this project is only intended to be a Windows desktop application. Press F9 or the Run button of the IDE.

User Interface Structure

  • At the top of the UI there is a panel that provides static information about a fictional corporation.
  • Beneath the top panel are two panels containing the following:
    • The left hand panel contains a Navigator control at the top while a TListView occupies most of the lower part of the panel and displays information about the corporation's branches.
    • The right hand panel contains a Navigator control at the top while a TListView occupies most of the lower part of the panel and displays a list of employees working at the currently selected branch from the left hand panel.

Using the Program

  • You can navigate to any of the displayed entries by clicking on the desired list item.
  • You can also navigate to any of the displayed entries by clicking on the Navigator buttons to move the selection.
    • The first four buttons in each navigator, Top, Up, Down and End move the corresponding List's selected item.
    • The next two buttons, Insert and Delete execute the corresponding actions in their corresponding List.
    • the final four buttons, Edit, Post, Cancel and Refresh respond, but in the present implementation seem to have no effect. They have been left for completeness although you can easily hide them since they currently serve no useful purpose.
  • When the selected item for Branches (the left ListView) changes, the Employees ListView (the right ListView) changes to display only the employees at the newly selected Branch.
  • Clicking the Add button (+) will insert a new item into the corresponding list using arbitrary values as placeholders. Each new entry has a number that advances by one for each new entry inserted.
  • When a new Branch is inserted, a blank Employee list is displayed as there are no employees for that branch. New employees may be inserted, however, for the newly inserted Branch.
  • Clicking the Delete button (-) will delete the corresponding selected entry after you respond Yes to a safety confirmation prompt. If you delete a Branch, all of its employees are deleted as well.
After a few changes, the UI might appear to be something like the following (a full-sized .png of this screen shot is also to be found in the Docs folder of the Github project or downloaded here):

LiveBindingObjects.exe User Interface After Some Data Changes User Interface
LiveBindingObjects.exe User Interface after some data changes.

Program Structure

This is a simple program, but it does have structure. 

MVVM, MVP and MVC

There are endless explanations, debates and disagreements on the internet about these three design paradigms. Yet all three paradigms seem to share the same fundamental functionality:
  • The presentation layer, or "VIEW" should contain as little logic as possible. They are difficult to test, and hence should be kept as simple as possible.
  • The data layer is handled by something called the "MODEL" that manages the data used by the application. It essentially hides the concerns about data from the VIEW. 
  • Between the VIEW and the MODEL is something that is variously called the VIEW-MODEL, the PRESENTER or the CONTROLLER. These three variations give the paradigms their distinctive names. It acts as an intermediary between the VIEW and the MODEL.
Arguing about the exact functioning or relative merits of these (and perhaps other similar) design paradigms is about as useful as arguing about how many angels can dance on the head of a pin. What seems to be universally true, however, is that if you bear in mind that the ultimate objective of these and other similar design paradigms is the separation of concerns, you won't be far off with designing well-structured, maintainable code.

Thus, for example, if you are building an application that screens loan applications, you will probably want to separate the code that queries the major credit bureaus from the code that updates the local database with the loan status. It makes no difference whether you call this a VIEW-MODEL, or a MODEL, or  CONTROLLER so long as you maintain a clean separation concerns you will probably end up with an easily maintained, testable application.

I would further submit that Delphi and Delphi's RAD is not well-suited to the ways that these current paradigms are understood, anyway, and attempting to put Delphi's round peg into a square hole will result in worse, not better structure and certainly a lot more code, much of it unnecessary. Just maintain separation of concerns and avoid putting code in the VIEW and you'll be well along toward producing quality, maintainable, testable code.

The demonstration program has two major areas of concern:
  1. Model and Database that manage the data used by the program.
  2. View that is the User Interface that presents data to the user and receives input from the user that is then used to drive the Model to perform the desired functions.

Model and Database

The MODEL is implemented by the file named MDULBO.pas that contains a single class that descends from TDataModule. It is a TDataModule descendant to permit dropping components on its design surface. The Delphi automatic form creation mechanism for this object has been disabled. Creation and Destruction is handled by the application code, not the Delphi provided automatic code.

Ancillary to this basic MODEL is file DBLBO.pas, that defines the three classes (all descended from TObject) that define the actual data used by the program. By creating these classes, the database is created. Also a part of DBLBO.pas are an enumeration (not used by the demo program) and three TypeDefs that will be explained in a later blog. The constructors for the database classes are also responsible for populating the created objects with appropriate data. Thus, database definition and initial population are all contained in these three classes. There is a more comprehensive discussion of these classes in a subsequent blog.

View

The view is implemented by the unit contained in the file VULBO.pas. It contains a single class, descended from the customary TForm ancestor. The automatic form creation for the VIEW has not been disabled and functions as expected. The VIEW contains a number of component definitions, and a pointer to the MODEL, but other than that, it contains almost no code, making it quite easy to test.

This concludes the present discussion. In the next post, we'll take up the Database portion of the MODEL in greater detail.

No comments:

Post a Comment

FireMonkey String Grid Responsive Columns

FireMonkey String Grid Responsive Columns Code to Cause Column Widths to Change When String Grids Are Resized Overview I have a FireMonke...