CTO does lazy load. Film at 11.

ctodx
25 October 2007

Sometimes a CTO needs to get back to coding. Here's something that came up in an internal discussion.

Suppose we have this code:

    
    public class HeavyFoo {
      public HeavyFoo() {
        // big honking constructor
      }
      public void DoSomething() {
      }
    }
  
    public class UserOfHeavyFoo {
      private HeavyFoo heavyFoo;
      public UserOfHeavyFoo() {
        // don't want to create heavyFoo here
      }
      public void UseHeavyFoo() {
        if (heavyFoo == null) 
          heavyFoo = new HeavyFoo();
        heavyFoo.DoSomething();
      }
    }

We imagine we have this useful class called HeavyFoo. Unfortunately its constructor does some heavy processing, maybe by setting up some connection to somewhere, setting up a bunch of data, reading a file, or whatever. So we'd rather create a HeavyFoo object only when we really, really need it. This is exactly what the UserOfHeavyFoo.UseHeavyFoo() method does: if the internal field hasn't been initialized yet, go ahead and create the HeavyFoo instance. This pattern is known as the lazy load pattern.

All well and good, but it imposes a hidden contract on the developer of the UserOfHeavyFoo class: you can never "just" refer to the heavyFoo field, you always have to make sure that it's initialized beforehand.

So people traditionally refactor this code to something more like this, to use a read-only property:

  
    public class UserOfHeavyFoo {
      private HeavyFoo heavyFoo;
      private HeavyFoo HeavyFoo {
        get { 
          if (heavyFoo == null) 
            heavyFoo = new HeavyFoo();
          return heavyFoo;
        }
      }
      public UserOfHeavyFoo() {
        // don't want to create heavyFoo here
      }
      public void UseHeavyFoo() {
        HeavyFoo.DoSomething();
      }
    }

At a superficial level this seems to help, but we still have a hidden contract: when we write a new method, we should never use the heavyFoo field and always use the HeavyFoo property. If we don't, the code will still compile, but it may fail in weird ways at run-time depending on the order of execution of our new method vs. UseHeavyFoo(). Call the UseHeavyFoo() method first and everything works, call the new method first and you'll get a crash.

What we'd like to do is to make sure that we can't access the bare heavyFoo field. The only way to do that is to take it out of the UserOfHeavyFoo class and put it somewhere else. My current idea is along these lines:

  
    public class LazyLoad<T> where T : class, new() {
      private T item;
      public T Item {
        get {
          if (item == null)
            item = new T();
          return item;
        }
      }
    }
  
    public class UserOfHeavyFoo {
      private LazyLoad<HeavyFoo> heavyFoo;
      public UserOfHeavyFoo() {
        heavyFoo = new LazyLoad<HeavyFoo>();
      }
      public void UseHeavyFoo() {
        heavyFoo.Item.DoSomething();
      }
    }

This is much better in one way since we can no longer refer to an uninitialized HeavyFoo instance in our UserOfHeavyFoo class, but it looks a little awkward in another: we now have a double dereference when we want to do something. Also, the LazyLoad generic class can only deal with classes that have a constructor with no parameters; something I think is unlikely for heavy duty constructors. We can avoid both of these by having a specific lazy load class for HeavyFoo, but I can imagine that if we were to have a series of these lazy load classes, there would be a lot of code and behavior duplication.

So I'm still thinking about this one, although I'm beginning to think that HeavyFoo has something to do with the code smell.

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.
Tags
No Comments

Please login or register to post comments.