t e m p o r a l 
 d o o r w a y 

Decoupling Data Modules And Forms To Aid Reuse

 

Introduction

As you develop systems in C++ Builder you will probably find dependencies between the various data modules and forms are preventing you from attaining higher levels of reuse. This document discusses some strategies for reducing or avoiding those dependencies.

Types Of Coupling

Coupling among forms and data modules falls into a number of categories:

  1. Forms which refer to data sets in a data module (usually for lookups).
  2. Forms which call upon methods of other forms (often to get data from the form, or to force the other form to update) or which reference data members of other forms (again, usually to get data from the form).
  3. Data modules which update forms as a result of data changes.
  4. Data modules which call upon methods of controls on forms or reference controls on forms.
  5. Data modules which refer to other data modules, usually for lookup or calculated fields.

The coupling can be manifested in either:

  • References to other form / data module components via properties of components in this data module or form.
  • References to other form / data module variables, methods, or components by event handlers or methods in this data module or form.

Each type of coupling requires a different approach. Not all coupling can be eliminated.

Using Design Reduce Coupling

Creating your system from decoupled forms and data modules is a good start. Then, you can add the coupling to lower levels of a form inheritance hierarchy and still preserve the independence of the top level.

In terms of designing data modules, it is always a good idea to start with your ER diagram, and create a data module for each entity. Each entity data module should be self contained - that is, it should contain:

  • The primary table for the entity.
  • All queries based on the entity primary table.
  • All domain tables for the entity (that is, tables from which values for attributes of the primary table will be drawn).
  • All lookup tables for the primary table and the domain tables.

Each entity typically has a relationship with one or more entities. This is where cross data module coupling first arises. There are two alternatives for dealing with relationships.

  1. Make the relationship table a member of a third data module which is coupled to the two or more related entity data modules.
  2. Make the relationship table a member of the entity data module.

An example of this sort of coupling exists when two entity primary tables must be joined to produce data to be displayed in a grid. Obviously, this can be accomplished either through a query or through a set of lookup fields on a primary table which use another entity's lookup table as the lookup data set. In either event, it is likely that the only inhabitant of a "relationship" data module would be the query or the augmented primary table (either having lookup fields referencing the non-primary table), or perhaps, the augmented primary table and any needed lookup tables.

Many relationships are either uni-directional, or can be viewed as a pair of uni-directional relationships (going in opposite directions). This allows either strategy to work.

Generally, most relationships are "pull" relatioships, where only the current entity knows enough to make the relationship work. For this reason, I prefer strategy 2. As you will see later, this also has a good conceptual fit with other elements of a decoupling strategy.

Using Data Awareness To Reduce Coupling

Data modules are the most sharable part of an application above the component level. For a form to be coupled to a data module is not an undesirable form of coupling, nor is it always one which can be eliminated, especially if you wish to avoid cluttering your form with datasets and datasources.

The most common form of undesirable coupling involves a data module which updates a form when a data event occurs. In almost all cases, this can be avoided through the use of existing data aware components, or through the creation of a new data aware component. For instance, if you need a label on a form which shows the current number of rows in the current view of data from a dataset, you could have the OnDataChange event handler for the dataset data source update the label. Or you could derive a data aware label component, hook it to the data source and then pull the record count from the associated data set. This is the preferred course, since it reduces coupling, and where coupling exists, lays it on explicit properties of the form.

Using Inheritance And Public Members To Reduce Coupling

Some situations may require a data source event handler or a data set event handler to call upon a form, whether to update the form (for which data awareness is the better solution) or to use the members of the form class or its components to perform some computation or action. Coupling in this situation can be reduced by having the data module or form maintain public members, preferably properties (so lazy evaluation can be used) which are accessible from other form or data module processing.

To make this reduce coupling, it is best used with a derived version of the data module or form, with the ancestor having none of the offending code or members. This improves reuse potential since the ancestor can be reused separately from the coupled form or data module, and the coupling is clearly delineated in the descendant.

Using The Redirector Pattern To Reduce Coupling

A redirector is a component or member whose sole function is to direct references within code to what may be a different other component as time passes or in different implementations. For instance, if a data module must access a form control, that control can be assigned to a data module property by an application form after both the form and the data module are instantiated. Or a component can be used to embody the redirection, by having a property for the control in question. This allows a default component to be set at design time, which can then be easily overridden at run time (published properties cannot be added to data module descendants, so this pattern overcomes that obstacle). In code, the module references Redirector->Control rather than SomeForm->Control.

This pattern also makes it easier to construct test harnesses in the presence of this sort of coupling.

Another useful aid provided by redirector components can be to have an event handler which is invoked when the destination is changed. Such an event handler can cascade the change to any other affected properties in components of the form or data module, keeping knowledge of those properties within the form or data module.

Summary

There is no panacea for reducing coupling. However, these techniques should be helpful.

Copyright © 2004 by Mark Cashman (unless otherwise indicated), All Rights Reserved