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.