Can Good Object-Oriented Design include Data-Aware Controls?

By: Wayne Niddery

Abstract: Many claim data-aware controls cannot co-exist with correctly designed object-oriented code. I intend to dispel that myth.

Can Good Object-Oriented Design include Data-Aware Controls?

by Wayne Niddery
Logic Fundamentals, Inc.

The following discusses components used in development with Delphi and C++Builder, but the principles discussed apply in general to the use of data-aware controls as provided in any development environment.

There have been many discussions, primarily in the oodesign newsgroups about whether it is permissible or advisable to use data-aware controls in a properly designed object-oriented project.

Many argue they must not be used, that they violate various principles of OO design and are a practical source of problems as a result. Many seem to think that data-aware controls directly access the database in some way and thus can bypass business logic. This would be a serious flaw and would indeed invalidate their use in a good OO design. But this has never been true, data-aware controls have no power, on their own, to do such a thing.

Data-aware controls are actually very passive and simply inform their (built-in) datalink class when something happens. It is the datalink class that transports the data back and forth between the actual controls and the data source. The datalink class performs the role that must be performed in any case and it is primarily the logic in this class that must be re-invented by those who reject the use of data-aware controls.

Moving data between controls and a data source in no way compromises an OO design because data sources (i.e. TDatasource) do not directly connect to a database and have absolutely no knowledge of the ultimate source or destination of the data that passes through them. Their only role is to provide a single point of connection for any number of individual data-aware controls. That single point of connection is to any descendant of the TDataset class.

All the classes to this point are actually very well thought-out designs and, far from violating OO principles, they very much uphold those principles in terms of abstraction and data hiding, low coupling and cohesion (that their design could be even better given newer language features such as interfaces does not invalidate the positive aspects of their original design).

This leads us to one last level, the TDataset class. This is the class that actually talks to a database. Well actually TDataset itself does not, but it is designed with this capability in mind so that descendant classes can be developed that actually talk to specific databases. TQuery and TTable are TDataset descendants that talk to the Borland Database Engine. TIBDataset is a TDataset descendant designed to talk directly to the Interbase API. Thus it is only at this level where we reach a point of physical connection with a database.

On its own, TDataset is still not a problem; it is also a well-designed and very powerful abstraction and ultimately lies at the base of even those OO designs that forbid the use of data-aware controls. After all, not too many would wish to re-invent the functionality and abstraction provided by the TDataset class, and there is no need to.

Where then is the problem?

There is indeed a problem, but the problem does not lie at any one single level among the classes discussed above. The blame heaped upon the poor, misunderstood data-aware controls is terribly misplaced. The problem lies in the routing of data. The argument that data-aware controls have any direct coupling or dependency on the database those controls are reflecting only has validity when they are, via their data source, hooked up to a dataset that is connected to an actual database. Even then any blame put on the data-aware controls is misplaced since their nature and behavior does not change, but only the route data takes to get to them and to get back to the database.

The exact issue is this: If data can move between data-aware controls and a database without part of their route being directly through a set of business objects that have full control over the data being moved, then the design, no matter how good otherwise, can rightly be considered compromised. Unless the business objects are inherently part of the conduit, then it is possible to get around them.

So what is the solution?

First and foremost, sacrificing data-aware controls is not it. This is treating the symptoms instead of the disease.

It can be argued that, because all the classes discussed, especially TDataset, provide ample events and properties, business classes can adequately enforce control over the data through these means. For some projects this may be enough and thus justifiable. But this solution requires the business classes to operate on the terms provided by the existing TDataset, et al, classes. They, in a sense, sit on the side and exert control as best they can as the data flows by them.

Ideally, it is the business classes that set the terms and the other classes are subject to them. The obvious way to achieve this is for the business classes to hide datasets behind their abstraction. By doing this it is not possible for data-aware controls, and potentially presentation layer (GUI) logic, to make an end-run around the business logic (by having their data sources hooked directly to those datasets). The presentation layers only source of data becomes the business classes themselves thus making the business classes a central pillar of the design.

If datasets are hidden from the view of data sources, then it seems data-aware controls must be sacrificed!

There are three solutions to this I will discuss:

  • The business classes can, under their control, grant access to a dataset in order to allow data-aware controls to be connected.

    Although the emphasis or perspective is different, this solution is really no different in principle than that above where business classes only exert control from the side of the road. No matter how it is worded, granting such access to a dataset is essentially granting permission to bypass the business logic.

  • A new hierarchy of object-aware control classes can be developed that are able to hook to properties provided by business classes.

    This solution works and may even have some advantages. But it is definitely re-inventing a set of classes that already work. The logic of transferring data back and forth between the business classes and the controls is no different in principle or in practical scope than the logic currently existing between standard data-aware controls and datasets. All data actions, View, Insert, Update, Delete, must be handled, and there are a lot of controls that have to be developed for object-awareness including grids and their standard behaviors. The business classes, or additional helper classes for the purpose, must present varying degrees of dataset-like interface to the presentation layer. I personally do not consider this a very productive solution.

  • Business Classes can define, populate, and provide memory-only ClientDatasets to the presentation layer.

    There are several advantages to this solution.

    • There is absolutely no compromise to the integrity of business classes because the ClientDatasets will have no direct connection to a database-connected dataset. The data processed by such a ClientDataset has only the business class as its endpoint and no way to bypass it.
    • It does not require the re-invention of data-aware controls. The existing set of data-aware controls (including all available 3rd-party products) are made compatible with the goals of well-designed business classes.
    • The familiar and very usable concept (pattern!) of a dataset, including editing functions, is preserved at the presentation level, but again, without compromising the integrity of business classes, the business classes have absolute lordship over what happens to data in the ClientDataset..
    • This solution works equally well in two-tier and multi-tier designs (data can be passed between ClientDatasets).
    • The data provided in a ClientDataset will reflect the requirements of the business class, not an underlying database. The fields and records defined in a ClientDataset need not have any relation to actual fields or tables in a database, the business class is free to invent the layout and contents any way it needs.
  • An example

    Here is a trivial example that shows how easy it is to manufacture a record definition and populate it with data. The following code can be compiled and a TDatasource and data-aware controls can be connected to this ClientDataset to see the records. A user can, if you allow it, insert, edit, and delete records. More importantly, no matter what the user does, the data goes nowhere except to the business objects and is thus strictly subject to the rules of the business classes.

     
    with ClientDataset1 do begin
      FieldDefs.Add('FirstName', ftString, 40); 
      FieldDefs.Add('LastName', ftString, 40);
      FieldDefs.Add('Phone', ftString, 20);
      CreateDataSet;
      Append;
      Fields[0].AsString := 'Wayne';
      Fields[1].AsString := 'Niddery';
      Fields[2].AsString := '(905)555-9283';
      Post;
      First;
    end;
    

Summary

Existing data-aware controls are perfectly compatible with well-designed object-oriented systems. The scorn placed on them by many has been misplaced; the real problem is the routing of data; data-aware controls hooked to datasets that have actual database connections is the problem since this allows data to flow around business logic instead of through it. But it is a problem solved easily by making your business classes responsible for creating the datasets seen by the presentation layer.


Server Response from: ETNASC01