Programming Decorator + Notification Pattern

Recently, I was asked to rework the code which handled our companies internal expense tracking for projects.  Before I get into the solution let me first describe the workflow and some details a long with it:

  1. Expense entry is submitted for a valid project so long as it falls within the budgetary guidelines for the project
  2. The entry is then saved as an Unapproved expense to such a table within the database. If this save is occurring on a such a date as to make the expense late an email is sent out as well
  3. The expense is then reviewed by the project manager and approved or rejected.  When approved it is moved from the Unapproved table to the Approved table

Pretty simple for the most part.  The one thing that irked me was because it used two tables it used two different entities.  This would mean that I would have to know which entity I wanted to work with, that is where I started to consider the Decorator pattern.  The reason for this is, I don’t feel that I should care about which entity I am updating, that code already exists, I just need to determine if I can call it.  For the most part the two entities share many common property, just with different names, so they can easily be united using an interface. In addition, the union of properties between the two, I also made visible Save and Delete routines.  This way I could create my decorator containing my single reference to the underlying entity via a abstract type:

public class ExpenseEntry : IExpenseEntity, INotifyPropertyChanged
{
     private IExpenseEntity _entity;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

This is the definition for the Decorator class which I chose to call ExpenseEntry.  Notice how it implements our IExpenseEntity interface, which guarantees that interacting with the parent is the same as interacting with the underlying entities. Remember the decorator contains properties that are designed to talk to underlying object.

One of the requirements is that we want to know when certain properties change as it may or may not throw the underlying object into a invalid state depending on context.  Basically, business rules state that only administrators can modify the entry once it has been approved.  This class contains a simple boolean flag to specify whether the class is being used in such a context. However, the case exists that within a given week an approved and unapproved expense can exist, so we need to make sure we are leaving approved expenses alone and not notifying the user of failure unless they try to modify it.  This is where the Notification pattern comes in, in that we want to make sure that we have an easy and clean way to notify the parent that something changed and respond appropriately.  Here is the code in the Decorator for handling the event:

public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(T oldValue, T newValue)
{
  PropertyChangedEventHandler handler = PropertyChanged;
  if (handler != null && !oldValue.Equals(newValue))
  {
    handler(null, new PropertyChangedEventArgs(string.Empty));
  }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

The important thing to see here is that if the two entries match we do NOT fire the event as there would be no need to do so.  Notice we are using generics so that we can use this handler with a variety of data types, this is because the properties we will be monitoring also vary in type and our goal is to have a single point of monitoring.

If the event is triggered the handler simply flips a boolean member variable (_dirty) to true.  This variable comes into play when the context is examined as Save is called, an exception is raised if the state is not valid for saving.

This is nothing new to may developers in the world, nor is it necessarily anything spectacular.  But I am slowly beginning to see the fruits of my interaction with the other intelligent manifest themselves, through more pattern thinking to the questions I ask and when I ask them. Initially this solution used a generic event handler which some more complex logic. I have since simplified it, but I recall commenting to a co-worker that before I came here I would never have dreamed writing code like this, but now it feels so natural. Evolution of the mind truly is a wonderful thing to behold.

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s