Nested classes

I’ve been programming against the .NET framework for a while, and I confess that I haven’t much used nested classes.  I’ve just never had a compelling need.  But today I was working on some unit tests, and found a nice example of how nested classes can be very helpful, and even make normal classes easier to use.

It all began with DateTime.Now.  It is very hard–no, impossible–to test objects that rely on DateTime.Now in a consistent way, because you have no direct control over the value of “Now”.  It is actually an external dependency, and like all external dependencies it is best to abstract it away for testing purposes.  So I created this rather mundane interface and a simple implementation class that I could pass to any object that needed to use the value of “Now”.

internal interface ISundial {
    DateTime Now();
    DateTime Today();
}

internal class Sundial : ISundial {
    public DateTime Now() {
        return DateTime.Now;
    }

    public DateTime Today() {
        return DateTime.Today;
    }
}

I know it smacks of over-engineering, but bear with me: this is just a preamble to real content.  Anyway, so now I have a nice ISundial interface which I can use for creating a FakeSundial object in my unit test.

internal class FakeSundial : ISundial {
    private DateTime _now;

    public FakeSundial(DateTime now) {
        _now = now;
    }

    public DateTime Now() {
        return _now;
    }

    public DateTime Today() {
        return _now.Date;
    }
}

This worked great for a number of tests. I could guarantee that “Now” would be any value that I needed it to be, and then I could perform assertions to verify the results of date calculations, for example.

Some tests failed, however, and I immediately knew what was wrong.  The Now() method, by design, never advanced with the system clock, whereas DateTime.Now does.  This caused me some headaches, so I added a few convenience methods to simulate the passage of time.

internal class FakeSundial : ISundial {
    private DateTime _now;

    public FakeSundial(DateTime now) {
        _now = now;
    }

    public DateTime Now() {
        return _now;
    }

    public DateTime Today() {
        return _now.Date;
    }

    public void FastFowardSeconds(int seconds) {
        _now = _now.AddSeconds(seconds);
    }

    public void FastForwardMinutes(int minutes) {
        _now = _now.AddMinutes(minutes);
    }

    //etc.
}

This worked great and gave me the flexibility that I needed, but I still had to implement FastForwardMilliseconds(), FastForwardHours(), FastForwardDays(), FastForwardYears(), and all the damn RewindXYZ() methods that I knew I was going to need as well.  I began to alter my design a little bit. Perhaps I could just have generic FastForward() and Rewind() methods that took an int argument and some kind of enumeration to represent the various time measurements, and then build a glorious switch statement that would call the appropriate methods on the DateTime field.  But that, too, was rather kludgy, and not at all befitting for an important class such as FakeSundial.

Then I thought: wouldn’t it be nice if I could use a kind of fluent syntax to accomplish this same thing, something like:

var fakeSundial = new FakeSundial(DateTime.Now);
fakeSundial.FastForward(60).Minutes();

Two things were immediately apparent:

  1. FastForward() and Rewind() would need to return an object other than FakeSundial (otherwise, what would calling fakeSundial.Minutes() do on its own?); and
  2. that other object would need access to the private field _now in the FakeSundial instance.

The second point was the impetus for a nested type, because in C#, a class that is declared within another class has full access to the parent class’s private and protected members (much like an instance method can access the private instance variables of its declaring class).

A corollary to point #2 is that the nested class would need to be a private class, which would prevent other objects (whether children by inheritance or external to FakeSundial), from accessing the private state data of FakeSundial.  This is a good example of Encapsulation in OOP.  I decided to call this class SundialDuration:

private class SundialDuration {
    private readonly FakeSundial _fakeSundial;
    private readonly int _incrementValue;

    public SundialDuration(FakeSundial fakeSundial, int incrementValue) {
        _fakeSundial = fakeSundial;
        _incrementValue = incrementValue;
    }

    public void Seconds() {
        _fakeSundial._now = _fakeSundial._now.AddSeconds(_incrementValue);
    }

    public void Minutes() {
        _fakeSundial._now = _fakeSundial._now.AddMinutes(_incrementValue);
    }

    public void Hours() {
        _fakeSundial._now = _fakeSundial._now.AddHours(_incrementValue);
    }

    public void Days() {
        _fakeSundial._now = _fakeSundial._now.AddDays(_incrementValue);
    }
}

The class constructor takes an instance of the FakeSundial object, as well as an increment value, and performs the appropriate increment operation when the respective increment method is called. (If the increment value is a negative number, the value of “Now” will move backwards.)

The FastForward() and Rewind() methods in FakeSundial cannot have a return type of a private nested object, so I created a public interface that defined the increment methods in SundialDuration, and made it the return type of FastForward() and Rewind().  The full class implementation looks like this:

internal class FakeSundial : ISundial {
    private DateTime _now;

    public FakeSundial(DateTime now) {
        _now = now;
    }

    public DateTime Now() {
        return _now;
    }

    public DateTime Today() {
        return _now.Date;
    }

    public ISundialDuration FastForward(int howMany) {
        return new SundialDuration(this, howMany);
    }

    public ISundialDuration Rewind(int howMany) {
        return new SundialDuration(this, -howMany);
    }

    private class SundialDuration : ISundialDuration {
        private readonly FakeSundial _fakeSundial;
        private readonly int _incrementValue;

        public SundialDuration(FakeSundial fakeSundial, int incrementValue) {
            _fakeSundial = fakeSundial;
            _incrementValue = incrementValue;
        }

        public void Seconds() {
            _fakeSundial._now = _fakeSundial._now.AddSeconds(_incrementValue);
        }

        public void Minutes() {
            _fakeSundial._now = _fakeSundial._now.AddMinutes(_incrementValue);
        }

        public void Hours() {
            _fakeSundial._now = _fakeSundial._now.AddHours(_incrementValue);
        }

        public void Days() {
            _fakeSundial._now.AddDays(_incrementValue);
        }
    }

    public interface ISundialDuration {
        void Seconds();
        void Minutes();
        void Days();
        void Hours();
    }
}

Now, my unit tests can simulate the passage of time with a few method invocations:

var sundial = new FakeSundial(DateTime.Now);
var timeSensitiveObject = new TimeSensitiveObject(sundial);
timeSensitiveObject.DoSomething();
//simulate the passage of time!
sundial.FastForward(10).Days();
//operating in the future now
Assert.AreEqual(10, timeSensitiveObject.DaysElapsed);

7 thoughts on “Nested classes

  1. Ritch M says:

    Its a test fixture. Is spending time managing internal, private, etc… really worth the effort?

  2. It didn’t take me long to implement, and I did it more as an exercise in curiosity. I still needed to fake the passage of time; this is just a more elegant way to do it. Also, I make all types “internal” by default, and only change their accessibility to “public” as necessary. Since these are fake types used strictly for unit testing there is no reason to make them public.

  3. Ryan says:

    I’m trying to figure out whether this is very clever, or whether you’ve simply gone insane. 😀

  4. Ryan says:

    Alas, as there are no extension properties, the syntax would be uglier than that:

    sundial.Add(10.Days())

    Personally, I mostly use nested classes for things like Debug.Print-focused methods that won’t need to be public.

  5. I don’t really like the extension method approach, because seeing In32.Days()/Months()/Years() out of context would be confusing, I think.

Leave a Reply

Your email address will not be published. Required fields are marked *