Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Five Great Patterns for Dealing with References : Page 4

In the old days, when applications primarily consisted of a number of windows and a database, using reference data was still easy. You would create a reference table and refer to it from your main tables. Nowadays, in object-oriented environments, where your business logic is key, there are more alternatives for dealing with references.

Type Safe or Flexible?
So far, the patterns introduced are very useful when it comes to type safety. To enable this safety net the specific reference elements in these patterns are hard coded. However, situations may occur where type safety is less important than having the flexibility to change and add new reference elements without having to change the code. A simple example for this is reference types such as Countries or Currencies. Although the number of countries is not subject to many changes, whenever a new country emerges, it needs to be added immediately to the collection of elements. Thus, a second species of reference patterns arises, in which flexibility prevails security.

Small Business Class Pattern
Suppose you have a Location class that specifies a location where courses are held. Such a class would have a property Country that refers to a specific instance of a reference type Countries, such as in the next code example.

public class Location { public string Name; public string Address; public string City; public Countries Country; }

In this implementation of the Location class the property Country can be set to any valid instance of the Countries class. The Countries class is a small business class. New elements are hardly ever entered, existing elements rarely change and small business classes are mostly used as references. In most cases, the data of instances of small business classes are kept in a database, each small business class having an associated table in the database. Regardless of how the data is retrieved from the database, a small business class may look something like this.

public class Countries { public string Name; public string Description; public string CountryCode; … public static Countries New() { … } public static Countries Get(ID id) { … } public bool Delete() { … } public bool Update() { … } public static Countries[] All { get { … } } }

It goes without saying that display values for small business class can be combined from any of the available fields. Preferably, you could override the ToString() operation, and use this.

public class Countries { … public string ToString() { return Name + “ (“ + Code + “)”; } }

Note that in the database, each record in the Location table holds a foreign key to an associated primary key in the Countries table. Using a small business classes as reference allows for great flexibility. That is, it is easy to change or add an element, without having to recompile the application. Another benefit of using small business classes is that retrieving the collection of all instances is straightforward. It is not unlikely that a small business class holds a static property All (or likewise operation) that returns the collection of all countries, as an array of instances of Countries. And more, it is easy to inherit from a small business class and add additional functionality.

Using a small business class also has some drawbacks, especially when type-safe validations needs to be performed on specific fields of the class, like in the following code example from the Location class. In this example, a string is used for implementing the validation, again allowing typing errors—recall the constant collection.

public class Location { … private Countries country; public Countries Country { get { return country; } set { if (value.CountryCode != “NL”) country = value; } } }

If type safety is required, the Countries class can be extended with a number of static instances that can be used in validations. In the following code example the instance Netherlands is predefined.

public class Countries { … public static Countries Netherlands = Countries.Get("NL"); }

Now validation can be done using this static instance, hence it has the same syntax as validation using an enumeration or descriptor.

public class Location { … private Countries country; public Countries Country { get { return country; } set { if (value != Countries.Netherlands) country = value; } } }

Note that, since the instance Netherlands is declared static, the associated record will only be retrieved once from the database—when the first validation is executed. This extension technique is only useful when only a limited number of validations need to be performed. And although it offers more possibilities for static checking, it diminishes the flexibility of the solution, because these static instances may never be removed from the underlying table.

There's always a trade-off between usability (in code) and flexibility (in use). You'll find this trade-off is the main criteria when choosing one of these patterns.

Smart Reference Pattern
In the last pattern, a single small business class is defined. This single small business class is used to refer to any reference type that can be expressed in the properties of that class. Typically such a class, called a smart reference, has properties such as Name and Description. The single-most important property of a smart reference however, is the Type property, which declares which specific reference type is meant.

The Type property can best be expressed using an enumeration, for instance called ReferenceTypes, as in the following code example. Using this property, operations such as All() and Default() can be defined for retrieving all instances of SmartReference for a specific type, or even the default instance. Such operations can be declared both static and not static, either specifying the type, or using the instance's Type property.

public enum ReferenceTypes { Countries, Currencies } public class SmartReference { public string Name; public string Description; public ReferenceTypes Type; … public SmartReference[] All() { … } public static SmartReference[] All(ReferenceTypes rt) { … } }

This last pattern is extremely flexible. New elements can easily be added to any of the types in the ReferenceTypes enumeration. The main benefit of using a smart reference over a number of small business classes is that it saves you a lot of programming effort. Maintenance functionality for all your reference types consists only of a single form, allowing the user to select a reference type and maintain the elements of that type.

However there is a small price to pay for this maintenance flexibility. Using a smart reference does not automatically ensure type-safe validation. A property of type SmartReference will need to perform additional reference type validation, as demonstrated here:

public class Location { … private SmartReference country; public SmartReference Country { get { return country; } set { if (value.Type == ReferenceTypes.Country) country = value; } } }

Also the display value for a SmartReference is limited to the fields defined by the SmartReference class. Again, I suggest using the ToString() operation to set the display value to any combination of fields.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.