I'm looking for advice on how to handle constraints on the input of a method. I have a method that only works if the input argument(s) fulfill certain constraints. If the method is called on input that does not fulfill the constraint(s), the method does something harmful.
The concrete problem is this. I have a class Interval
which represents closed 1-dimensional intervals:
// code in C# or pseudo-code
class Interval
{
private double lb, ub; // lower bound and upper bound of the interval
public Interval(double lb, double ub)
{
if (lb > ub)
throw new ArgumentException("Lower bound cannot be larger than upper bound!");
this.lb = lb; this.ub = ub;
}
public bool Disjoint(Interval i)
{
return (ub < i.lb || i.ub < lb);
}
public void UniteWith(collection of Intervals)
{
...
}
}
The method UniteWith
should take a collection of disjoint Interval
s as input and modify it such that, when thinking of the collection as a union of sets, the current Interval
is joined to that set. Also, the collection should be modified such that after joining, it consists of disjoint Interval
s again.
Example:
collection is: [-3, 0], [2, 4], [5, 18], [21, 22]
current interval is: [3, 6]
resulting modified collection is: [-3, 0], [2, 18], [21, 22]
Methods I definitely need for the collection are Add
and Remove
, so ICollection
seems to be a reasonable choice for the input type (thinking in C# again, but probably applies to Java and other OOP languages, too).
For the algorithm to work correctly, I need the input to be sorted. If it is not sorted, the input will be modified unpredictably. And this is the point where I don't know what to do:
I could take
ICollection
as argument, but then I would need to check by myself if the input is sorted. If it is not sorted, I throw an exception to tell the programmer using my code that his input is bad. I believe I can do this by using the enumerator that everyICollection
has.public void UniteWith(ICollection<Interval> coll) { IEnumerator enumerator = coll.GetEnumerator(); enumerator.MoveNext(); Interval fst = enumerator.Current; while (enumerator.MoveNext()) { Interval snd = enumerator.Current; if (fst.ub >= snd.lb) throw new ArgumentException("coll must be sorted ascendingly!"); fst = snd; } // if we get here, we can start the actual algorithm ... }
I could make
coll
aSortedList
orSortedSet
in the method signature instead ofICollection
. Then I would be sure it is sorted, but now a user cannot input hisIList
even if he sorted it already, and since hisIList
provides all methods the algorithm is going to need (Add
andRemove
and iterating over it), he might be disappointed he cannot use it. Also, I don't know a way to tell the compiler "this input can be eitherSortedSet
orSortedList
", and if there is no such way, it is even more restrictive. Okay, I could write two methods for that. Then I had to take care of two documentation comments (I'm commenting EVERY method I have) and copy-paste changes all the time. It is possible, but doesn't sound very appealing to me.I could keep the input type
ICollection
, do not run any checks on whether the input is sorted, and advise in the documentation comment not to put any unsorted collection in there and explain the consequences of doing so. So I hand over the responsiblity to the user of my code. This does not seem very clean to me either, because this is exactly what C/C++ do (and what I don't like): They let you ignite the computer; they say they let you because they cannot be sure you did not want to, but in fact you did not want to - you just made a mistake.
Looking forward to any suggestions, not just for this concrete scenario, but also for the general case - if there is such a thing as a "general case". (Might as well be that it always depends.)
EDIT:
I just noticed that for the first and third option it might be better to only allow inputs that have a concept of order. Even though a HashSet
has an enumerator and as such an order, it is more an internal feature. From its concept, it has no order (it can change when adding / removing elements). So what interface can I put in there? Is there something like IOrderable
?