attern gurus are quick to remind us that patterns are not code.
Patterns exist at a level above the code. Or, as Martin Fowler says (PDF)
, "Patterns provide a mechanism for rendering design advice in a reference format." This implies that patterns are too abstract to be implemented in a generic fashion using object-oriented techniques. But there is a set of low-level patterns (some might even call these idioms) that you can implement in a reusable fashionnot with object-oriented techniques, but using code generation. In this article, I'll introduce you to just such a pattern, and then show you how to build a reusable implementation with .NET's CodeDOM namespace.
Part 1: Introducing the Property Union
The PropertyUnion pattern is very simple: It's a wrapper class that exposes all the relevant properties of a given inheritance hierarchy by flattening it so that client code doesn't have to do any type-checking or casting before accessing the properties of an object.
If that explanation makes no sense, that's alright; keep reading. If, on the other hand, you've used this pattern, or a similar one, then you can skip ahead to Part 2
and get started with the code generation.
|Figure 1. A Simple Domain Model: Employees need to store extra information beyond the Contact class, so the domain consists of two related classes.|
Object-oriented programming languages and databases can take very different approaches to modeling the same problem domain. Someone coined the phrase "impedance mismatch" to describe the difference in philosophy, and nowhere is the mismatch more evident than mapping inheritance hierarchies onto database tables. Since traditional relational databases don't support inheritance, you have to decide how to map your inheritance hierarchy onto the flat relational model.
Here's an example. Let's say you're building a simple contact management application. You want to work with plain old contacts as well as contact information for employees which need extra, employee-specific data. One solution is to create an inheritance hierarchy with two classes: Contact (a concrete base class) and its subclass Employee (see Figure 1
Now for the data model. The simplest solution is what the gurus call "single table inheritance." The idea is to create a single table for the entire hierarchy, with a set of fields that represents a union of all fields in the hierarchy, and a "type discriminator" field (either a string or an integer associated with an enum) to let users know which type each row represents (see Figure 2
Table 1 shows a representative set of rows from the sample data. Notice how the shaded fields are applicable only to contacts of type Employee.
Table 1. The table shows the intersection of properties from the Contacts and Employees tables. The unshaded columns apply to all contacts, while the shaded columns apply only to contacts of type Employee.
|Figure 2. An Even Simpler Data Model: In the database, you want to have only one table, but when you flatten the hierarchy in this way, not every field is applicable to every record.|
This approach has a few advantages over a data-model which uses multiple tables:
- Simplicity. Reports are easier to write against a single table.
- Performance. A single table means that the data-layer only has to hit the database once for every type of employee.
- Maintainability. With a single table, you can refactor properties into different levels of your object model without affecting your data model.
Now, it's time to write the data-layer. The most obvious way to create objects based on rows from the database is to switch on the type discriminator. The following data-layer method builds a Contact or an Employee object from a strongly typed datarow.
private static Contact BuildContact(
Contact contact = null;
contact = new Contact();
Employee asEmployee = new Employee();
asEmployee.Salary = contactRow.Salary;
asEmployee.MailStop = contactRow.MailStop;
asEmployee.Title = (Employee.JobTitle)
contact = asEmployee;
throw new Exception("Unknown ContactType");
That's not too pretty. And it's naïve, because the problem gets far worse when it's time to build the user interface, as you can see in the following UI-layer code, which fills out various UI widgetsagain, based on the type of the contact object being edited.
private void EditContact(Contact contact)
Employee employee = contact as Employee;
bool isEmployee = employee != null;
this.tbEmployeeNo.Enabled = isEmployee;
this.tbMailStop.Enabled = isEmployee;
this.nudSalary.Enabled = isEmployee;
this.cbTitle.Enabled = isEmployee;
this.dtpBirthDate.Value = contact.BirthDate;
this.tbMailStop.Text = employee.MailStop;
this.cbTitle.SelectedItem = employee.Title;
The duplicated logic in the methods shown above should trigger some warning bells. In fact, you could end up writing the same code as many as four times because you're creating and modifying Employee objects and Contact objects in both the data layer and in the UI.