Sunday, 18 December 2011

I followed through on my threat to do a presentation on using tiny objects. My supposition is that most developers (myself included) resist creating trivial (tiny) objects even when they could clean up code. Objects don’t have to possess lots of properties or methods to become useful.

Let’s take this example…

Random _randy = new Random();
public List<double> TestSeriesPoints(int numberOfPoints, double minValue, double maxValue)
{
    if(minValue >= maxValue)
    {
       throw new Exception("minValue must be less than maxValue");
    }

    List<double> points = new List<double>();
    for (int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++)
    {
        double point = _randy.NextDouble() * (maxValue - minValue) + minValue;
        points.Add(point);
    }
    return points;
}

It is a pretty straightforward method that returns some test data within the range of a min and max value.  It’s a short method, and pretty easy to grok, so 99 times out 100, it wouldn’t be given a second glance.  But there is a tiny object buried in here, one that will not only make this code cleaner, it will make modifying it easier too.

Any time we see two variables that are always used in tandem, it is time to make a Tiny Object…

Random _randy = new Random();
public List<double> TestSeriesPoints(int numberOfPoints, double minValue, double maxValue)
{
    if(minValue >= maxValue)
    {
       throw new Exception("minValue must be less than maxValue");
    }

    List<double> points = new List<double>();
    for (int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++)
    {
        double point = _randy.NextDouble() * (maxValue - minValue) + minValue;
        points.Add(point);
    }
    return points;
}

Where there’s a min value, there’s bound to be a max value. They don’t exist without each other, so let’s make the commitment and formalize the relationship…

public class DoubleRange
{
    public double MinValue = 0;
    public double MaxValue = 100;
}

By combining two floating (but always proximate) variables into a single Tiny Object, we’ve pulled ourselves out of procedural thinking and opened up the OOP world.  We could leave it like this and still get some some benefits.  But by its mere creation, our new Tiny Object has given us something concrete to work with and re-use. It gives us a place to add behaviors and properties that consuming code will no longer have to repeatedly re-implement.

Once this object has materialized, it becomes apparent we can ensure that our range is always valid by using a constructor and encapsulated properties.  And we can turn the actual range of our DoubleRange into a property, so the consuming code does not have to recalculate it. Finally, we can also create default constructor that sets our range to have common min and max values.

public class DoubleRange
{
    private double _minValue;
    private double _maxValue;

    public DataRange():this(0,100)
    {
    }

    public DataRange(double minValue, double maxValue)
    {
        if(minValue >= maxValue)
        {
             throw new Exception("minValue must be less than maxValue");
        }
        _minValue = minValue;
        _maxValue = maxValue;
    }

    public double MinValue
    {
        get{return _minValue;}
    }

    public double MaxValue
    {
        get{return _maxValue;}
    }

    public Range
    {
        get{return MaxValue - MinValue;}
    }
}

Our consuming code can boil off some logic that lives in the Tiny Object, so it looks like this…

Random _randy = new Random();
public List<double> TestSeriesPoints(int numberOfPoints, DoubleRange testDataRange)
{
    List<double> points = new List<double>();
    for (int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++)
    {
        double point = _randy.NextDouble() * testDataRange.Range + testDataRange.MinValue;
        points.Add(point);
    }
    return points;
}

Our Tiny Object has grown into something useful…

  • the consuming code is clearer, working with a DoubleRange object, not some implicitly related values
  • the unexplained (maxValue – minValue) expression in our calculation is explicitly defined in its new object home
  • the object handles its own validation

Finally, adding one small method to our Tiny Object will also make debugging  and logging easier…

public override ToString()
{
   return string.Format("Range {0:n4} to {1:n4}", _minValue, _maxValue);
}

Now, any time the object is added to a log statement, we’ll always see the current range values like so…

Range 0.0000 to 100.0000

That gives us three of the four joys of OOP -- abstraction, encapsulation,  and polymorphism – all in one Tiny Object. 

So the next time you see two or three variable spending a lot of time together, do the right thing move them into their own Tiny Object and see what other  wonderful attributes and behaviors blossom.

And always remember there are no Tiny Objects, only tiny developers.

OOP
Sunday, 18 December 2011 16:09:56 (Eastern Standard Time, UTC-05:00)   #     Comments [0]  | 

Theme design by Dean Fiala

Pick a theme: