A common need in domain modelling is to specify a range - of dates, of prices, or some other numeric measure. Typically, we model this with a pair of properties, as in this example:

class MarketingCampaign
    DateTime? EffectiveFrom;
    DateTime? EffectiveUntil;

To go along with these properties, there is typically a bunch of minor logic used to ensure the range remains valid. One way is to throw an exception if an invalid range would be formed (say, if EffectiveFrom was set to a date after EffectiveUntil); another is to potentially update both ends of the range to ensure it remains valid (setting EffectiveDate to a date after EffectiveUntil would also update EffectiveUntil to the new date).

An alternative that provides more functionality and more reuse is to use an explicit Range<T> struct to declare the property:

class MarketingCampaign
    Range<DateTime> Effective;

Of course, the interesting bits are in the Range struct:

public struct Range<T>
    where T : struct, IComparable<T>
    public T? From { get; set; }
    public T? To { get; set; }
    public bool IsBounded { get; }
    public Range();
    public bool Contains(T value);

The Range struct is pretty conventional, but has a couple of features worthy of note.

Either bound may be omitted if no limit is required in that direction. In our MarketingCampaign example, To can be omitted if the end date of the campaign has not yet been determined.

Also, the sequence of the bounds is maintained - setting the value of From lower than To will update To to ensure the Range is legal, and vice versa, as follows:

public T? To
    get { return mTo; }
        mTo = value;
        if (mTo.HasValue 
            && mFrom.Value.CompareTo(mUpperBound.Value) > 0)
            mFrom = mTo;

As you can see, Range is a simple helper - but one that makes our properties just that bit more expressive, capturing our domain semantics more accurately.

There are possible extensions of course. Perhaps the most obvious would be for Range to properly implement the INotifyPropertyChanged interface, exposing a PropertyChanged event for subscription. In all likelihood, the parent class would end up subscribing to this property itself in order to correctly trigger it’s own PropertyChanged events.

Another, more complicated approach would be to create a RangeProperty helper similar in intent to the Property helper discussed in my recent post Improving on POCO Properties.


blog comments powered by Disqus
Next Post
CI, Builds and NAnt  15 Jan 2010
Prior Post
Improving on POCO Collections  08 Jan 2010
Related Posts
Error Methods  25 Nov 2017
Pass implementations, not representations  14 Oct 2017
Avoiding the Singleton Pattern  22 Jul 2017
Implementing the Singleton Pattern  15 Jul 2017
Static Analysis tools for the Win  15 Apr 2017
Exception Logging  19 Jan 2016
On the Merits of Simple Code  28 Nov 2015
Semantic Types  27 Sep 2015
Command Line Processing  21 Sep 2014
Easy String Conversion  24 Aug 2014
More smart-code posts »
Related Pages
January 2010 archive