Friday, July 30, 2010
 
   
 
Welcome to my site

First let me say thanks for stopping by my site. My name is David Hanson-Graville and I am a IT consultant working in the UK. Let me make it clear, I am passionate about technology and specifically .net and its various forms. I've programmed in a range of langages, but I can say, I am now at my happiest when coding with c#. I hope my blog is an enjoyable & educational read and please feel free to email me at David.Hanson@OnTheBlog.net if you have any questions. 

Defensive Coding Minimize
Location: BlogsOnTheBlog    
Posted by: David Hanson Fri, 05 Jun 2009 06:33:18 GMT
How often have you seen code like this?
 
private void AllocateRoles(RoleChangeInfo roleChangeInfo)
{
            foreach (Role role in roleChangeInfo.Roles)
            {
                if (roleChangeInfo.Person.Age > 21 && roleChangeInfo.Person is Senior)
                    RoleChangeService.AddRole(rolechangeInfo.Person, role);
            }
      }
 
 
If we deconstruct this code we can see that the method AllocateRoles should received an instance of a RoleChangeInfo which is a container for all the information that is required to add the roles to the appropriate person. If we take a look at the implementation of this type we can see that it’s fairly simplistic.  It holds the ID of the Role and a RoleTaken DateTime.
 
public class Role
{
    public int ID { get; set; }
    public DateTime? RoleTaken { get; set; }
}
 
The body of the AllocateRoles method consists of a simple ForEach iteration around the Roles collection and with each role we check to make sure the person is older than 21 and they are of type senior. If both of these conditions are true then we call the RoleChangeService.AddRole method which will perform the work and update the RoleTaken.
 
I have come across this kind of code many times in the past and it is a good example of where a developer has made a number of assumptions and have not thought about making the AllocateRoles method defensive.  So before we jump into making this code defensive let’s take a look at some of the possible issues with this code.  Can you see them?
 
Developer Assumptions
·         That roleChangeInfo will not be null
·         That the Roles collection will be initialised
·         That each role within the Roles collection will not be null
·         That the roleChangeInfo.Person will not be null
·         That it is ok to call this method when the Person is not a senior
·         That it is ok to call this method with when the person is younger than 21
·         That it is ok to call this method and no roles are present.
·         That it is ok to exit this method without checking to see if our Role.RoleTaken has been set.
·         None of the Roles.RoleTaken stamps have already been set.
 
From the list of items above you get the idea.  The code in AllocateRoles takes the path of least resistance and should invalid data be passed in no specific exceptions will be raised. This can result in two possible outcomes. Firstly, the code crashes with a null reference exception and we spend hours trying to find the cause of the issue or the code executes a scenario that is considered invalid. This can lead to data corruption and a large headache trying to figure out where it’s happening.  
 
Enter Defensive coding – When we apply defensive coding techniques to this method we are trying to attain 2 possible outcomes. Firstly we wish to identify all possible errors within our method and throw exceptions if any of them are met. Secondly, we want to ensure that no business functionality will be executed accidently and that the results of our code is sound. Given this, let’s take another stab at our AllocateRoles method.
 
private void AllocateRoles(RoleChangeInfo roleChangeInfo)
{
      //CHECK: that we have a valid rolechangeInfo and that we have a Person and that the person
      //is of type Senior and that the roles collection is not null.
if (roleChangeInfo != null && roleChangeInfo.Person != null
    && roleChangeInfo.Person.GetType() == typeof(Senior)
    && roleChangeInfo.Roles != null)
{
         // CHECK:  that we actuall have roles.
    if (roleChangeInfo.Roles.Count == 0)
        throw new ApplicationException("You must specify at least one role.");
 
    foreach (var role in roleChangeInfo.Roles)
    {
  // CHECK:  we dont have a null role.
        if (role == null)
            throw new NullReferenceException("Role cannot be null");
  // CHECK:  that the Role Taken has not been set.
        if (role.RoleTaken != null)
            throw new ApplicationException("Role has already been taken.");
  // CHECK:  that the Persons ages is not less than 21.
        if (roleChangeInfo.Person.Age < 21)
            throw new ApplicationException("You cannot allocate roles to seniors under 21");
 
        //DO OUR WORK HERE
        RoleChangeService.AddRole(rolechangeInfo.Person,role)
 
    }
 
    // CHECK:  all roles have a taken date.
    if (roleChangeInfo.Roles.Exists(role => role.RoleTaken == null))
        throw new ApplicationException("Role taken date has not been set.");
}
}
 
As you can see the method implementation above now performs a number of checks on the input data and where appropriate throws exceptions if the state is invalid.  At this point we could consider this method defensive enough for our purposes. However by implementing numerous checks on the state of the data we have lost readability of our business logic.  Secondly the checks are scattered throughout the method and the very first check still falls through and can exit with no code being run. In most cases I may not mind this but others I may want to ensure fall through does not occur. The result is that I would have to break apart this check into multiple If’s. Not ideal.
 
Enter Arrange, Act, Assert -  If you are familiar with unit testing you may have come this pattern when creating your tests. If you haven’t I suggest you have a quick read here. But the basics of this pattern are that you arrange your code in a way so that the Act you are performing can execute with the desired effect and that after the work is done you can assert any failures.  Once you get the hang of it it certainly makes writing tests easier. Given this, I want to refactor the method so that a reusable pattern can be established when performing defensive coding. If we look at our pattern is follows the following workflow.
 
Assert (Input) >> Act >> Assert (Output)
 
Given this pattern and I what I want to achieve, I needed to create a simple utility class that would allowing me to perform daisy chaining of conditions which can then be executed at a given point. The Defensive methods such as IsNull would return instances of themselves allowing semantics such as the following.
 
Defensive.CurrentMethod
    .IsNotNull(test, "Message")
    .IsInstanceOfType(new object(), typeof(object), "Type does not match")
    .IsTrue(() => 1 == 2, "1 wont equal 2")
    .Check();
 
Using this new utility class we can now go back to our AllocateRoles method and refactor to meet AAA pattern we use in our tests. Below is the final defensive method.
 
private void AllocateRoles(RoleChangeInfo roleChangeInfo)
{
    //ASSERT Inputs
    Defensive.CurrentMethod
        .IsNotNull(roleChangeInfo, "Cannot call AllocateRoles without a roleChangeInfo")
        .IsNotNull(roleChangeInfo.Person, "You must have a valid Person reference")
        .AnyNull(roleChangeInfo.Roles, "You must specify roles")
        .IsInstanceOfType(roleChangeInfo.Person, typeof(Senior), "You can only apply roles to senior people")
        .IsTrue(() => roleChangeInfo.Person.Age > 21, "You cannot allocate roles to seniors under 21")
        .IsTrue(() => roleChangeInfo.Roles.Count > 0, "You must specify at least one role.")
        .Check();
 
    //ACT
    roleChangeInfo.Roles.ForEach(role => RoleChangeService.AddRole(rolechangeInfo.Person, role));
 
    //ASSERT Outputs
    Defensive.CurrentMethod
        .IsTrue(() => !roleChangeInfo.Roles.Exists(role => role.RoleTaken == null), "Role allocation failed.")
        .Check();
}
 
You can see that the AAA syntax coupled with the daisy chaining allows for our defensive checks to be grouped together at the top and bottom of our method. This avoids diluting our business code with numerous checks. Defensive coding is a powerful tool in our arsenal when it comes to increasing the resilence of our application code.
Permalink |  Trackback

Comments (2)   Add Comment
Re: Defensive Coding    By Kane on Sun, 13 Dec 2009 09:39:46 GMT
Hey dude, you got any code for the "Defensive" static class?

Re: Defensive Coding    By host on Mon, 14 Dec 2009 09:47:21 GMT
Posted for you.


Your name:
Title:
Comment:
Security Code
Enter the code shown above in the box below
Add Comment   Cancel 
Tweets Minimize
Twitter / LordHanson
  1. LordHanson: Flash on iPad....nice. http://www.tipb.com/2010/07/04/frash-android-flash-ported-ipad/

    Published Sun, 04 Jul 2010 22:07:55 +0000 by
  2. LordHanson: Anyone noticed that when typing on your iPhone it sounds like your holding a gieger counter?

    Published Sun, 04 Jul 2010 22:05:28 +0000 by
  3. LordHanson: Missing wacko's music... What's happened to the album he was working on before he died?

    Published Fri, 25 Jun 2010 23:01:45 +0000 by
  4. LordHanson: New version of Connectify cannot recognise my active Internet connection! Had to roll back to previous version! #fail

    Published Fri, 25 Jun 2010 22:54:54 +0000 by
  5. LordHanson: vuvuzela blowing spoils the world cup! Fact!

    Published Mon, 14 Jun 2010 05:08:43 +0000 by
  6. LordHanson: About http://www.theaustralian.com.au/business/news/us-competition-regulators-to-investigate-apple/story-e6frg90x-1225878779986

    Published Mon, 14 Jun 2010 00:04:45 +0000 by
  7. LordHanson: In the camper van and a storm is coming.....How exciting.

    Published Sun, 25 Apr 2010 04:39:48 +0000 by
  8. LordHanson: My vaio p is doing well while travelling. 3g Internet, HD movies, digital tv, photo editing, wifi router for iPods and much more. Love it

    Published Wed, 10 Mar 2010 10:29:19 +0000 by
  9. LordHanson: Ok so I need to stay techie while away from a computer for a year. Anyone got any ideas.

    Published Mon, 22 Feb 2010 12:31:09 +0000 by
  10. LordHanson: Sitting in YHA Glebe Sydney waiting for the movie night to start

    Published Thu, 18 Feb 2010 08:13:10 +0000 by
  11. LordHanson: I finished work today in prep for travelling. I must admit as i left the office i felt a little emotional. Sign of a good job with great ...

    Published Tue, 26 Jan 2010 17:48:49 +0000 by
  12. LordHanson: Lots of sick sounding people creeping onto the trains today. Stay away stay away!

    Published Fri, 22 Jan 2010 08:14:36 +0000 by
  13. LordHanson: LMAO RT @colmbrophy: so have you seen who owns http://wwwbing.com ? it's not microsoft

    Published Tue, 19 Jan 2010 17:35:43 +0000 by
  14. LordHanson: RT @TeemuHyn: RT @eldarmurtazin: Hate Windows Mobile 7. No apps from previous versions of WM working here. Not compatible at all.

    Published Sun, 17 Jan 2010 19:01:29 +0000 by
  15. LordHanson: The girlfriend has gone all science on me.... She has her head in books about quasars!

    Published Sat, 16 Jan 2010 16:38:58 +0000 by
  16. LordHanson: New blog post: Invoking generic methods with Expression.Call http://bit.ly/5g99Ih

    Published Sat, 16 Jan 2010 16:34:00 +0000 by
  17. LordHanson: Found a nice way to invoke generic methods using expression trees and importantly avoiding Type.Getmethods() which you normally have to do!

    Published Fri, 15 Jan 2010 18:55:38 +0000 by
  18. LordHanson: Tried a dummy run of packing my backpack last night. Just managed to get it all in. The sleeping bag takes half the space. Not good.

    Published Fri, 15 Jan 2010 08:28:47 +0000 by
  19. LordHanson: Why do we get headaches for no apparent reason?

    Published Tue, 12 Jan 2010 15:28:29 +0000 by
  20. LordHanson: @swhelband beer time my friend

    Published Sun, 10 Jan 2010 21:49:44 +0000 by
Print  
Archive Minimize
Print  
Contact me Minimize
Print  
Microsoft Certs Minimize







Print  
Silverlight News Minimize
Silverlight - Google News
  1. Top 10 Things I Wish I Knew Before I Started My Silverlight 4 Project - Redmond Developer News

    Published Thu, 29 Jul 2010 23:21:01 GMT+00:00 by
  2. Microsoft's Flash challenger Silverlight hits Symbian - Register

    Published Tue, 06 Jul 2010 18:54:39 GMT+00:00 by
  3. Windows Phone 7 misses big-business support tools - Register

    Published Mon, 26 Jul 2010 20:32:23 GMT+00:00 by
  4. Neustar Reports Q2 Results, Stock Repurchase - TMCnet

    Published Fri, 30 Jul 2010 13:13:57 GMT+00:00 by
  5. Intertainment begins commercial rollout of Ad Taffy - PR Newswire (press release)

    Published Fri, 30 Jul 2010 14:19:22 GMT+00:00 by
  6. Download Microsoft Expression Studio Ultimate 4.0.20525.0 Free Trial / Full ... - Soft Sailor (blog)

    Published Fri, 30 Jul 2010 08:50:54 GMT+00:00 by
  7. Managing change in the application portfolio - Register

    Published Thu, 29 Jul 2010 09:36:57 GMT+00:00 by
  8. Queen Victoria in Liverpool panoramic picture - Liverpool Echo

    Published Mon, 26 Jul 2010 11:56:30 GMT+00:00 by
  9. AEBN's Silverlight Player Gains Traction with Users - AVN News (press release)

    Published Tue, 27 Jul 2010 20:28:37 GMT+00:00 by
  10. Written by David Conrad - iProgrammer

    Published Tue, 27 Jul 2010 12:39:37 GMT+00:00 by
Print