Implementation question about enum "RangeBoundaryType"

Topics: Enterprise Library Core, General discussion, Validation Application Block
Apr 19, 2007 at 1:24 PM
Maybe this kind of question is a little bit off-topic, but if anyone involved in the design/implementation of the enum "RangeBoundaryType" (namespace "Microsoft.Practices.EnterpriseLibrary.Configuration.Design.Validation") is reading this, then I am interested to see an explanation of why it has been implemented as an enum instead of a struct ?

(I am generally very skeptic to using enums in OO languages and think that methods and data should be put together rather than having external methods executing if statements on the enum, and if you instead would have a struct/class you would be able to refactor the if statements to instead use polymormoic behaviour through the "State pattern").

For further details, see this posting in MSDN forum:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1493691&SiteID=1

Apr 19, 2007 at 5:53 PM
I don't know what such a struct would look like. RangeBoundaryType is an enum since it's a single piece of data that can have one of several discrete values (Inclusive, Exclusive or Ignore). Structs are basically the same as classes (containers of multiple pieces of data and behaviors) that are stored on the stack instead of the heap. It could be argued that we could have used a class or struct to encapsulate the concept of a Range, independent to the validator itself, but I can't think of any clean ways (or sensible reasons) to avoid the use of an enum for the tri-state nature of the range boundaries.

Could you give a code example of your proposed struct that doesn't use enums?

Tom
Apr 20, 2007 at 10:13 AM

tomhollander wrote:
I don't know what such a struct would look like. RangeBoundaryType is an enum since it's a single piece of data that can have one of several discrete values (Inclusive, Exclusive or Ignore). Structs are basically the same as classes (containers of multiple pieces of data and behaviors) that are stored on the stack instead of the heap. It could be argued that we could have used a class or struct to encapsulate the concept of a Range, independent to the validator itself, but I can't think of any clean ways (or sensible reasons) to avoid the use of an enum for the tri-state nature of the range boundaries.

Could you give a code example of your proposed struct that doesn't use enums?

Tom


Hmm, then I guess I do not have the latest version of the RangeBoundaryType, since my only has Inclusive and Exclusive (not any "Ignore").
Anyway, I indeed think that yes, you should have encapsulated the concept of a Range into a class, since C# enums can not be
used as OO abstract data types, i.e. C# enums encourages a procedural programming style.
(Java enums are real OO types, since they let you put methods that belong to the data togeher into the same type).


>I can't think of any clean ways (or sensible reasons)

Is not there a reason to use an OO abstract data type (ADT) when you have a concept that is appropriate as an ADT ?
I thought Microsoft have a strong preference for OO programming instead of procedural programming, but maybe that is a misunderstanding.
In OO, there is a concept called Abstract Data Type, which tell you to put methods and data together in the same type.
With Java enums, you can do that, but with C# enums you can not, which encourages procedural programming, as the RangeBoundaryType is an example of.
So you maybe do not think that a range/interval concept is appropriate as an ADT, but rather it is okay to have them spread out with multiple variables that are used together and will need to be duplicated together if used elsewhere (two such variables are "lowerBound" and "lowerBoundType", see below) ?
Then you might want to check out the Domain-Driven Design project timeandmoney
http://timeandmoney.domainlanguage.com/
where there indeed are classes representing intervals, and which are encapsulating the lower/upper limits and also the Inclusive/Exclusive concepts (with variables such as "lowerIncluded") i.e. these two concepts (limit values plus inclusive/exclusive information) are not being used in parallell as two separate items that belongs together and are used together and should be encapsulated within an ADT with methods that uses the data.

It is this kind of codelines that illustrates the procedural programming style that often occurs when using C# enums:
        int lowerBoundGreaterThanValue = lowerBound.CompareTo(compareToObject);
        if ((lowerBoundGreaterThanValue == 0) && (lowerBoundType == RangeBoundaryType.Exclusive))  addError = true;
        if (lowerBoundGreaterThanValue > 0) addError = true;
The "lowerBound" and "lowerBoundType" belongs together and both of them should be encapsulated within a boundary object, and the external
class/method "AssertRangeAttribute.ValidateCore" should not have to pull out the type checking in an if statement ("lowerBoundType == RangeBoundaryType.Exclusive") since that conditional behaviour would be better encapsulated within an abstract data type, e.g. by using polymorphism and subclasses to let you avoid the conditional.

>Could you give a code example of your proposed struct that doesn't use enums?

Well, sorry but I will choose the class instead of a struct since I prefer the polymorphic implementation (and struct can not be subclassed). So here are the kind of classes that I think should replace the enum:
The baseclass encapsulates the boundary values, while the subclasses decide whether the boundary values will be included.
(which also could be encapsulated with a variable, and in that case I could have used a struct instead)

 
 
    public abstract class Range
    {
        private readonly IComparable lowerBound;
        private readonly IComparable upperBound;
 
        public Range(IComparable lowerBound, IComparable upperBound)
        {
            if (null == lowerBound) throw new ArgumentNullException("lowerBound");
            if (null == upperBound) throw new ArgumentNullException("upperBound");
            if (lowerBound.CompareTo(upperBound) > 0)
            {
                throw new ArgumentOutOfRangeException("lowerBound > upperBound");
            }
            this.lowerBound = lowerBound;
            this.upperBound = upperBound;
        }
 
        // the polymorhic method implemented by subclasses
        public abstract bool IsSatisfyingRange(IComparable compareToObject);
 
        protected bool IsEqualToLowerBound(IComparable compareToObject)
        {
            return compareToObject.Equals(this.lowerBound);
        }
 
        protected bool IsEqualToUpperBound(IComparable compareToObject)
        {
            return compareToObject.Equals(this.upperBound);
        }
        
        protected bool IsStrictlyWithinInterval(IComparable compareToObject)
        {
            return (this.lowerBound.CompareTo(compareToObject) < 0) && (compareToObject.CompareTo(this.upperBound) < 0);
        }
    }
 
    public class RangeInclusive : Range
    {
        public RangeInclusive(IComparable lowerBound, IComparable upperBound)
            : base(lowerBound, upperBound)
        {
 
        }
 
        public override bool IsSatisfyingRange(IComparable compareToObject)
        {
            if (IsStrictlyWithinInterval(compareToObject))
            {
                return true;
            }
            else if (IsEqualToLowerBound(compareToObject))
            {
                return true;
            }
            else if (IsEqualToUpperBound(compareToObject))
            {
                return true;
            }
            else 
            {
                return false;
            }
        }
    }
    
    public class RangeExclusive : Range
    {
        public RangeExclusive(IComparable lowerBound, IComparable upperBound)
            : base(lowerBound, upperBound)
        {
        }
        public override bool IsSatisfyingRange(IComparable compareToObject)
        {
            if (IsStrictlyWithinInterval(compareToObject))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
    
    public class RangeIgnore : Range
    {
        public RangeIgnore(IComparable lowerBound, IComparable upperBound)
            : base(lowerBound, upperBound)
        {
        }
 
        public override bool IsSatisfyingRange(IComparable compareToObject)
        {
            // not sure what this would mean, but guess anything would be accepted ?
            return true;
        }
    }



Now, in you class AssertRangeAttribute, you currently have this:
    public class AssertRangeAttribute : ValidationAttribute
    {
        private readonly IComparable lowerBound;
        private readonly IComparable upperBound;
        private readonly RangeBoundaryType lowerBoundType;
        private readonly RangeBoundaryType upperBoundType;

These variables belong together and should be replaced with this one thing:
    public class AssertRangeAttribute : ValidationAttribute
    {
        private readonly Range range;
(Well, okay actually my implementation did not enable different exclusive/inclusive types for the upper/lower boundary, but only uses the same for both boundaries, but of course that could be fixed in a similar way)

Then in the "ValidateCore" method, the folloowing kind of code:
            int lowerBoundGreaterThanValue = lowerBound.CompareTo(compareToObject);
            if ((lowerBoundGreaterThanValue == 0) && (lowerBoundType == RangeBoundaryType.Exclusive))  addError = true; 
            if (lowerBoundGreaterThanValue > 0) addError = true;
would be replaced with:
          if(!range.IsSatisfyingRange(compareToObject))
          {
          	addError = true;
          }
Apr 20, 2007 at 4:16 PM
Thanks for providing the extra detail. The RangeBoundaryType applies separately to the upper and lower bounds. If we wanted to follow your pattern and support all possible ranges, you would have:
public class ClosedInclusiveRange : Range
public class ClosedExclusiveRange : Range
public class OpenRangeWithExclusiveLowerBound : Range
public class OpenRangeWithInclusiveLowerBound : Range
public class OpenRangeWithExclusiveUpperBound : Range
public class OpenRangeWithInclusiveUpperBound : Range

...which I hope you agree is impractical in the extreme. In general I agree that a Range abstraction would be useful, although it would still use enums to represent the different boundary types:
public class Range<T>
{
    public T LowerBound { get; set; }
    public RangeBoundaryType LowerBoundType { get; set; }
    public T UpperBound { get; set; }
    public RangeBoundaryType UpperBoundType { get; set; }
    public bool IsInRange(T value);
}

This is great OO (IMHO), however it doesn't take into account the usage pattern for the RangeValidator. In the overwhelming majority of cases, validators are configutred using attribute constructors or configuration classes, neither of which would allow users of the validator to call the Range class. So even if we had such an abstraction, we would still need to break the Range up into its component pieces for use on the attribute and config classes as we do today, and the Range would only really be used internally. So I don't think it would really buy much.

Tom
Apr 23, 2007 at 8:12 AM

tomhollander wrote:
Thanks for providing the extra detail. The RangeBoundaryType applies separately to the upper and lower bounds. If we wanted to follow your pattern and support all possible ranges, you would have:
public class ClosedInclusiveRange : Range
public class ClosedExclusiveRange : Range
public class OpenRangeWithExclusiveLowerBound : Range
public class OpenRangeWithInclusiveLowerBound : Range
public class OpenRangeWithExclusiveUpperBound : Range
public class OpenRangeWithInclusiveUpperBound : Range

...which I hope you agree is impractical in the extreme. In general I agree that a Range abstraction would be useful, although it would still use enums to represent the different boundary types:
public class Range<T>
{
    public T LowerBound { get; set; }
    public RangeBoundaryType LowerBoundType { get; set; }
    public T UpperBound { get; set; }
    public RangeBoundaryType UpperBoundType { get; set; }
    public bool IsInRange(T value);
}

This is great OO (IMHO), however it doesn't take into account the usage pattern for the RangeValidator. In the overwhelming majority of cases, validators are configutred using attribute constructors or configuration classes, neither of which would allow users of the validator to call the Range class. So even if we had such an abstraction, we would still need to break the Range up into its component pieces for use on the attribute and config classes as we do today, and the Range would only really be used internally. So I don't think it would really buy much.

Tom



>the Range would only really be used internally. So I don't think it would really buy much.

I think a new Range abstraction could buy you higher cohesion (i.e. having more focused responsibilites of the classes/layers) which is generally a good thing to do. The way I understand the enterprise library classes, a new Range class could be more generally reusable if it was an abstraction of it own, rather than being implemented within in a subclass of a framework class (System.Attribute).

>If we wanted to follow your pattern and support all possible ranges, you would have:
>...which I hope you agree is impractical in the extreme

Well, I do not believe it would be impratical to implement if it is modified as being an implementation of the composite/interpreter (GoF) patterns, using a combination of the following five disjunct "ranges":
(instead of those I initially suggested, or the ones you examplified with above)
SmallerThanLowerBoundary
EqualToLowerBoundary
StrictlyWithinRangeBoundaries
EqualToUpperBoundary
GreaterThanUpperBoundary

I believe this would be something like the "Composite Specification" pattern:
(though I do not understand the smalltalk examples and have not myself experimented with a .NET implementation neither)
http://www.martinfowler.com/apsupp/spec.pdf
(page 10-13)
Here is a quote from a part of the above pdf that seems a bit similar to what I am talking about:
"Here we will define just one very versatile one, ValueBoundSpecification, to be a named
value that expresses a limit on an attribute identified by the name. Lower bounds, upper bounds and
exact values may be defined with separate subclasses, or with case logic. Using these primitives, a
range can be expressed by a composite of a lower bound and an upper bound."


>In general I agree that a Range abstraction would be useful, although it
>would still use enums to represent the different boundary types

Yes, that kind of implementation of a Range would indeed be an option to what I suggested (or some "Composite specification" implementation) and when you get into the lower layers, eventually you will usually use some primitive types in your own low level abstractions (e.g. enums instead of integers).
Basically, I think I am just annoyed by the fact that C# enums are limited (compared to Java where you can attach methods to them)
and tends to lead to procedural programming, since when you realize you would like to add behaviour to the abstraction represented by the enum, then you will have to do it from outside of the enum type.