1

I often encounter the following program while programming. There is a "partial" solution to a problem, which provides all the functionality that is necessary, but the documentation explaining said functionality and the behaviour of the solution is very dense and esoteric. Then, there is a "full" solution, which provides more functionality than is necessary, but the documentation doesn't have to explain why it doesn't work in certain cases, and thus is very simple and easy to understand.

Just as an off-the-cuff example, consider a method Foo(A a) that takes a single argument of type A. Let's just say there's an easy implementation of Foo, but it requires A to have certain properties, and the explanation for why those properties are necessary is long and complex.

The "partial" solution is at the beginning of Foo I simply check if A has the required properties, and then reject it if it doesn't. This, however, makes the documentation very complex as now I have to explain why it doesn't work for certain objects.

On the other hand I have the "full" solution, which is simply to make Foo work for all As, regardless of their properties. Suppose I theoretically could implement a Foo that accomplishes this, but it requires a lot more code than the "partial" implementation.

Which is better?

Maybe that's a bad example, but I run into generalizations of this problem very frequently in a lot of my larger projects. Which solution should I choose when faced with this dilemma (if either of the "partial" or "full")? Are there any general tips for preventing this situation from arising?

  • are you aware of liskov-substitution principle? I guess not, otherwise you'd see why "partial" solution is not acceptable – gnat Apr 19 '16 at 7:59
  • 2
    Possible duplicate of What can go wrong if the Liskov substitution principle is violated? – gnat Apr 19 '16 at 8:03
  • 2
    @gnat, what does this have to do with Liskov Substitution Principle? I don't see any mention of inheritance in the question at all! – Jan Hudec Apr 19 '16 at 8:11
  • 1
    @gnat That's not my question. I'm not asking about the ramifications of violating the Liskov Substitution Principle, I'm not worried about the Liskov Substitution Principle in the slightest. I'm asking, if you have a complicated solution with easy documentation, and an easy solution with complicated documentation, which do you choose. Does my new example not make sense? – user3002473 Apr 19 '16 at 8:12
  • 1
    @gnat, ah, yes, that would be. But it is just a special case of the actual question (and it was obvious from the beginning that it is). – Jan Hudec Apr 19 '16 at 8:27
5

This is actually an easy one.

Do your requirements state that you need to be able to process As without the limiting properties? (Okay, everything is relative, so I'll ask: do you need the capability badly enough to pay for the extra development time?)

If yes, then write the extra code.

If no, then code the simple solution with the more complicated precondition.

Preconditions on APIs are a drag, but if you document them there is no question of having done anything improperly. Elegant solutions are nice, but they have a cost, and every cost has to be justified. "I apologize for writing such a long letter, but I lacked the time to make it shorter" can be a valid excuse in professional setting.

2

A lot of times, people have problems like this and think it's a design problem with Foo or its enclosing class, but it's really a design problem with A.

As an example, let's say A is a database connection. Common preconditions on database connections are that they point to a valid IP address, contain valid authentication information, etc. If you had to check every precondition upon every function call, it would get tedious, so a common pattern is to split the class. One class you use while you're putting in the server address and the authentication information, then from that object you validate the preconditions once and create another object which is used for executing queries.

In other words, instead of having the same object in a different state, look for opportunities create a new object for a different state. This eases both your precondition checking and your overall complexity.

Usually if your type hierarchies are designed properly, the situations where you have to choose a heavily-preconditioned implementation are fairly rare. If it's coming up a lot in a code base, the time it takes to get your types into shape will be well worth it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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