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.
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.
Connect with Us