Sunday, April 25, 2010

Design Patterns - Chain of Responsibility

Introduction


I am a fan of design patterns.  Patterns provide a common language and understanding of code-bases, and they help us solve problems in useful, understandable and maintainable ways.

Let's take the case I was working on a few months back.  A co-worker and I were given a project to produce some reporting.  The data being reported on was coming from 3 different data sources and needed to be normalized and scrubbed.  A decision was made to create a data-mart, where the data from the real-time production systems would be copied into the data mart weekly to allow managers to run large reports.

From discussions with managers during the discovery phase one thing became clear : There was general disagreement on how to scrub the data. This data deals with time reporting, and sometimes records didn't necessarily make sense - For instance, workers reported 'starting' and 'ending' work 1 week apart.  Clearly, they didn't work an entire week without rest, or food.  How should this record, then, be handled?  Should the number be truncated? Should we assume the worker meant the SAME day?  Should the record be thrown out? I know some of you out there are saying "Um, contact the worker and get him to fix it.".  In this case, that is not an option, and would likely be a post for a different site.  I think you can imagine that managers could think of an infinite number of ways to 'transform' the data before placing it into the data mart.

The Implementation


It is probably clear from the description about that a routine that using things like if/then's is going to be an error prone, and difficult system to maintain.  First off, when the first change comes in to modify how a record is handled, the developer will first have to find the correct if statement in a large chain of if's.  Second, the concerns are mixed together.  In addition, if a NEW type of data error is discovered that will require a new if statement.  Also, if a new variant on an existing error is discovered, a new if statement might be accidentally created if the developer making the change doesn't realize that the new requirement is simple a variant on an existing error.  Finally, given the amount of disagreement between the managers, it was clear that were going to go back and forth on what transformations they were going to want.  With the if/then implementation, this means a second level of if statements to determine if the transformation is needed, further obfuscating the intent of the code.

Design patterns to the rescue.  After a short discussion, it was clear that we wanted to use was the Chain of Responsibility (CoR).  The CoR describes a pattern that defines a series of processing objects and the operations they can perform, along with a way to add new processing objects on the end of the chain.

Let me explain how the solution was constructed.  Of course, I am not using the actual implemenation from work, but a variation on it in order to demonstrate the pattern.  First, we created an object that can needs to be operated on, in our case, I'll call it a Time object.  Then we created an abstract class called Transform that Takes a time object in its perform() method and a Transform object in its constructor. Then we created Transform implementations for the logical transformations needed.  Here are a couple of things the Implementations may have had:

  1. MultipleDayEntryTransformImpl
  2. ZeroTimeTransformImpl
  3. StartTimeAfterEndTimeImpl
  4. ExactlyFiveMinutesImpl
Here is some sample Java code:
/*Here is the class on which the Transform classes will operate.
*/
class Time {
   private Date start;
   private Date end;

   
   public Date getStart(){ return start};
   public Date getEnd() { return end};
   public void setStart(Date start){this.start = start };
   public void setEnd(Date end) {this.end = end};
}


/*Here is the abstract class that defines the interface and constructor
*/
abstract class Transform {
   /*
   */
   public Transform nextTransformation;
   public Transform(Transform nextTransformation){
      this.nextTransformation = nextTransformation;
   };


  public abstract perform(Time time);
}


/*Here is a simple, sample implementation
*/
class ZeroTimeTransformImpl {
  public perform(Time time) {
    if(time.getStart().equals(time.getEnd())) {
      //Do something because the record meets the criteria for this error
    }
    
    if(nextTransformation != null) {
       nextTransformation.transform(time);
    }
  }
}


/*Here is a partial class that might use these...
*/


class Importer {
  public static final main(String[] args) {
    //create the transformers.  this could be defined in
    //a DB, properties file, or made selectable through a GUI..
    ZeroTimeTransformImpl zeroTime = new ZeroTimeTransformImpl(new ExactlyFiveMinutesImpl(...any number of transform objects here));
    
     
    List
    //get Time objects, not implemented
    for(Time time : times) {
      //this will perform all the transformations in sequence
      zeroTime.perform(time); 
    }
    //store the normalized time records...
  }
}

As you can see from the code, the pattern has done a few things for us that are desirable when maintaining code:

  • The types of transformations are declarative and clear
  • The logic to determine if a transformation is needed, and what that transformation is are segregated from all other code and put in their own little world, making it very clear to the developer what is going on
  • Many new transformations can be added to the system without having to modify the main codebase at all.  A new instance and a change to the zeroTime instantiation, and you are done (The creation of the Transform objects can also be abstracted, but that's a different pattern.)
  • Business users can think up any number of transformations for the Time records, and the system should be able to handle it relatively easily.
  • The work of implementing the the transformations can easily be distributed among multiple developers without worrying about merges.
Best of all, I can tell my peers that we used a CoR pattern to solve this problem, and they will immediately have an idea on how the problem was solved, how the code is structured, and where to look then attempting to make changes.

Saturday, April 17, 2010

The problem is that it only allows 8 nested if...thens

The company I work for was recently purchased by a large corporation.  It seems to me that the Corporation does not view IT as a change-agent and value-enhancer, but rather as a cost center that the company wants to minimize.  Here are some things I have observed that lead me to that conclusion:
  • The day the purchase took effect, all 3 pending requisitions for new developers were cancelled, and a developer was let go.  This essentially took us from a team of possibly 7 down to a team of 3.
  • When another developer turned in his resignation, we were not allowed to backfill his position
  • The new company uses OLD technology and cheap alternatives, even when more expensive technology demonstrates an ROI
  • My boss, the Director of IT, is excluded from 'High level' meetings
  • Comments made by upper management suggest that they believe the quality of a $14/hr outsourced employee would be equivalent to that of an $185 specialist on that platform
In every interaction recently, it seems that the number one consideration for IT decisions is short-term cost.  In other words, what is the cheapest alternative.  I content that this leads to long-term failures and huge inefficiencies.  Here is an ancedote from my recent trip.

I was talking to a sales representative from the new Company (I'll call him 'Bob') and he asked me if I had "Seen the Excel Spreadsheet".  Bob continued that, "He was the one who made it."

Earlier that day I HAD been shown the Excel spreadsheet, and when he said 'the Excel Spreadsheet', I knew EXACTLY what he was talking about.  You see, the sales staff at this company had a pretty complex task to produce quotes for customers.  Each quote contained a complex configuration of inter-related components and it was taking them a long time to compile quotes.  But the company didn't want to spend money developing a quoting tool.

Bob took his knowledge of VBA and put it to use in Excel.  He had created a 12-sheet application within Excel that would accomplish the task he needed.   While the sheet seems to work, it needs a lot of care and feeding.  When prices are updated, configurations change, or new parts are added or removed, someone needs to copy that information into the Excel spreadsheet, and then the new version needs to be copied to all the other staff.  I'm not going to knock Bob. Hell, to see what he accomplished with VBA, I think that man is a genius. 

I still believe this is a broken system, though.  I didn't get to ask Bob how long it had taken him, but from the complexity of the application, I can only imagine that he had spent MANY hours developing his tool.  Hours that he, as a salesman, could have spent SELLING.   But even worse than that, if a programmer had been given the same amount of time to work on that project, the work product would be much better.  Likely it would be a Web application and the data would be integrated with the company ERP for starters.  The application could be maintained an enhanced and improved as the company grows and evolves.  Instead, there is now a stand-alone, one-off application sitting in the company infrastructure.  I am fairly certain the current Excel sheet is not going to be maintable going forward.  Why?  Because, when I asked Bob if he had written the entire thing in VBA he responded,

"Yes.  VBA is surprisingly powerful.  Its only problem is that it only allows 8 nested if...thens"

No, Bob, the problem is that you needed more than 8.

 

On Separation of concerns

I just returned home from a business trip.  I have also been tasked with completing a new application in a short timeframe. The application is basically a scheduling tool for our field staff. It needs to display data from our CRM tool.

I am working on this project with another developer who was not being sent on the trip.   Before I left, he and I sat down and architected the design of the app.  And this is how this post gets it's title - We made sure that the application was designed in such a way as to separate the concerns. Essentially, this application needs to do two things
  • Get Data about Field Engineers and their schedules
  • Present that data on-screen in a certain way
Our design session revolved around what Objects we needed, how they needed to be displayed, and what services would be needed to retrieve them from the data layer.  We then agreed upon and wrote up some Interfaces together.  From there we divided our tasks.

Since I was going to be out-of-town, I took the presentation layer.  My co-worker took the data layer.  While I was on my trip, I was able to construct an entire presentation layer and test it by creating Mock objects of the interfaces that returned dummy data.  My co-worker has been working on the data layer - implementing the interfaces with concrete classes that read from our CRM.

This is a simple description of what we did.  As I write this, I'm thinking about just how simple and logical it is.  It also amazes me at how powerful this simple strategy is, though.  This should be such a standard practice in development, yet so often it is not.   

I can't wait to check all this code in on Monday and see how they mesh together.