Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Using CodeDOM Code Generation to Implement Repetitive Patterns : Page 2

Implementing simple patterns is often boring, time-consuming, and error-prone—just because they're so simple. But by using code generation you can prevent a lot of the drudgery.


advertisement
Implementing the Property Union Pattern
So what do you do with duplicated logic? You encapsulate it. The PropertyUnion pattern moves all type-checking code into a single class, flattening out an inheritance hierarchy. Client code can then treat all types in the hierarchy uniformly.

In just a minute, I'll show you how the implementation works—if you haven't already guessed—but for now, let's look at the client code. Here's a much simpler data-layer method that's functionally identical, yet doesn't implement (or duplicate) any type-checking:

private static Contact BuildContact( Contacts.ContactRow contactRow) { ContactPropertyUnion.TypeEnum type = (ContactPropertyUnion.TypeEnum) contactRow.ContactType; ContactPropertyUnion union = new ContactPropertyUnion(type); union.Salary = contactRow.Salary; union.EmployeeNumber = contactRow.EmployeeNumber; union.MailStop = contactRow.MailStop; union.Title = (Employee.JobTitle) contactRow.Title ; union.BirthDate=contactRow.BirthDate; union.FamilyName=contactRow.FamilyName; union.GivenName=contactRow.GivenName; return union.Wrapped; }

You can see that the code above uses a PropertyUnion class instance to set the data. Now, here's a version of the UI method that also relies on a PropertyUnion class to enable widgets, and populate them with appropriate data—again, without type checking.

private void EditContact(Contact contact) { ContactPropertyUnion wrapper = new ContactPropertyUnion(contact); this.tbEmployeeNo.Enabled = wrapper.HasEmployeeNumberGetter; this.tbMailStop.Enabled = wrapper.HasMailStopGetter; this.nudSalary.Enabled = wrapper.HasSalaryGetter; this.cbTitle.Enabled = wrapper.HasTitleGetter; this.tbGivenName.Text=wrapper.GivenName; this.tbFamilyName.Text=wrapper.FamilyName; this.dtpBirthDate.Value = wrapper.BirthDate; this.tbEmployeeNo.Text = wrapper.EmployeeNumber.ToString(); this.tbMailStop.Text = wrapper.MailStop; this.cbTitle.SelectedItem = wrapper.Title; this.nudSalary.Value=wrapper.Salary; return; }

Conceptually, the PropertyUnion pattern is very similar to the object-oriented bedrock concept of polymorphism. Both techniques let client code ignore differences between related objects, but the two differ in the amount of information they make available. Polymorphism exposes the intersection of all available properties—that is, the set of members that all related objects have in common, the base type. PropertyUnion exposes, well, the union of all properties in an inheritance tree.

The ContactPropertyUnion Class
Here's another (admittedly informal) bedrock principle of object oriented design: If you have to do something ugly—and type-checking certainly qualifies—then at least put all that ugliness into a single place. The bad news is that the need to write type-checking code doesn't go away when you use the PropertyUnion pattern, but at least it's centralized. For this sample application, the ContactPropertyUnion class centralizes the type-checking code.

 
Figure 3. A New and Improved Object-Model: Each instance of the ContactPropertyUnion class contains an instance of type Contact.
This pattern is simply a wrapper class, called ContactPropertyUnion in this case, that instantiates either a Contact or an Employee based on a type-discriminator. So the class constructor shown below uses an Enum value instead of a magic number as its type-discriminator, but it's not doing any rocket science.



internal ContactPropertyUnion(TypeEnum toBuild) { if (toBuild == TypeEnum.Contact) { this.wrapped = new Contact(); return; } if (toBuild == TypeEnum.Employee) { this.wrapped = new Employee(); return; } throw new ArgumentException("Unknown enum value."); }

The ContactPropertyUnion class exposes all properties found on all the types that it wraps. Because not all wrapped objects actually implement every property exposed by the wrapper, each property does its own type-checking. If the wrapped object doesn't implement the property two things happen:

  • The getter method returns a default value.
  • The setter method throws away the value.
For example, the following code shows how the wrapper class exposes the EmployeeNumber property. As you can see, it's immaterial whether the underlying class actually implements the property—as a consumer, you can always pretend that it does without any penalty.

internal int EmployeeNumber { get { int outVal = new int(); if ( wrapped is Employee ) { outVal = (wrapped as Employee).EmployeeNumber; } return outVal; } set { if (wrapped is Employee) { (wrapped as Employee).EmployeeNumber = value; } return; } }

You'd repeat code similar to the preceding EmployeeNumber property for every property exposed by all the wrapped classes, or at least every property that client code might be interested in.

Finally, just to make it easy on any UI code, the ContactPropertyUnion class exposes Boolean properties that return true when the wrapped class implements a given property.

internal bool HasEmployeeNumberGetter { get { return wrapped is Employee; } }

Compared to the ubiquity of traditional polymorphism, the PropertyUnion pattern is something of a niche market. Here's when to use it:

  • In the data-layer. Use a PropertyUnion as a lightweight substitute for an Inheritance Mapper when working with a "single table inheritance" data model.
  • On the UI side. Use a PropertyUnion when you don't have a PropertyEditor control available—such as on Web Forms, for example—and your application's users might need to edit a number of related types.
  • When working with framework types. Some developers might argue that my object model above is naïve given the requirements. And they might be right, but you can't always control the design of the objects you're storing.
And here's when not to use a PropertyUnion:

  • As an excuse for inappropriate inheritance. Remember that inheritance is very easy to overuse, so don't use PropertyUnion as an excuse for inheritance when a more sophisticated pattern would be better.
The PropertyUnion pattern has some related patterns that you might want to explore if you're not familiar with them already.

  • Factory. Because the PropertyUnion can create its wrapped object based on a type discriminator, the constructor works as a factory method.
  • Adapter. The PropertyUnion flattens out an inheritance hierarchy, making it easier to use with a flat, relational structure.
By now, the PropertyUnion technique should be looking pretty easy. It's easy because it's all boilerplate code—no algorithms to speak of, and no reflection or anything fancy at all. In fact, it's so easy that you might be wondering whether there's a way to automate the implementation of a PropertyUnion pattern. There is.



Comment and Contribute

 

 

 

 

 


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

 

 

Sitemap