0

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 Intervals 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 Intervals 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 every ICollection 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 a SortedList or SortedSet in the method signature instead of ICollection. Then I would be sure it is sorted, but now a user cannot input his IList even if he sorted it already, and since his IList provides all methods the algorithm is going to need (Add and Remove 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 either SortedSet or SortedList", 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?

2 Answers 2

2
  1. ICollection with errors: I think the ICollection is the best option. However, it should be your responsibility to use the input properly, not the responsibility user of your code to give proper input. Avoid throwing errors unless there is no reasonable way to use the given input.

  2. SortedSet or SortedList: As you said, this is even more restrictive. If, for whatever reason, the input to the function is in a different form, you, again, should be responsible for properly using the input.

  3. ICollection without checks: While documentation is good, there are plenty of people that don't always read it. This approach could lead to errors that would take a long time to identify and solve.

Basically, it should be the programmer's responsibility that his/her code works, preferably with minimal restrictions. I'm not overly familiar with C#, but when I'm writing Java, I prefer to make my parameters Collections or Lists rather than SortedSets or Stacks. If I do need a SortedSet, I'll still ask for a Collection and then copy and sort it by converting it to a SortedSet or using built-in sort methods. In C# the List<T> class has a Sort method, and converting between Collection types doesn't seem too difficult, even if you need to use a for-each loop. If you care a lot about performance, and doing this conversion is too resource-intensive, I would use your second option (a SortedList or SortedSet). If writing each twice is too much work for you, you can pick one, and have the other one be converted and passed into the method, warning users that it would be less efficient.

Hope this helps!

2

The line if (fst.ub >= snd.lb), where you check if they are in order gives a clue to your solution. You know whether they are in order, so you know how to sort them. So have Interval implement IComparable<Interval> and add the following code:

public void UniteWith(IEnumerable<Interval> intervals)
{
    var sortedIntervals = intervals.OrderBy(i => i);
    ...
    // do stuff with your ordered set of intervals
}

public int CompareTo(Interval other)
{
    if (ub < other.lb) return -1;
    if (lb > other.ub) return 1;
    return 0;
}

Also, I'd question your use of void here. Would it not be better to create a new Interval and return that, leaving the current one unchanged?

1
  • Internally, I do create a new interval and leave the current one unchanged. Since the method is about modifying the collection, I don't see a reason for returning the new interval. Maybe I should return the modified collection instead, or a bool saying if the collection changed. So thanks for the hint!
    – Kjara
    Jul 28, 2016 at 5:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.