Flexibility

Some time ago, while I was working for a big software house, I heard this (presumably exaggerated) anecdote about how the company had charged a customer $1 million to upgrade the customer’s software. The customer had grown in size, and account codes required five digits instead of four. That was all there was to it. Of course, the client was almost certainly being ripped off, but there are plenty of examples in which a little lack of foresight proves very costly to repair. The Year 2000 problem is a prime example.

If you were negotiating a contract, the basic rules of business would dictate that you should try to obtain as much as possible and commit to as little as possible yourself. In the same way, if you view each class you write—even each function—as a contract, you should be making as few unnecessary commitments as possible in that code.

So if you need to pass the number of, let’s say, biscuits as a parameter to a function, why commit to only allowing fewer than 32,768 biscuits (the maximum value of an integer)? At some point, you might need to allow for half a biscuit too, so you wouldn’t want to restrict it to integer or long. You’d want to allow floating-point inputs. You could at this point declare the parameter to be of type double because this covers the range and precision of integer and long as well as handling floating points. But even this approach is still an unnecessary commitment. Not only might you still want the greater precision of currency or decimal, you might also want to pass inputs such as An unknown number of biscuits.

The solution is to declare the number of biscuits as a Variant. The only commitment that is made is about the meaning of the parameter—that it contains a number of biscuits—and no restriction is placed on that number. As much flexibility as possible is maintained, and the cost of those account code upgrades will diminish.

Function EatBiscuits(ByVal numBiscuits As Variant)
    ' Code in here to eat a number of biscuits
End Function

Suppose we want to upgrade the function so that we can pass An unknown number of biscuits as a valid input. The best way of doing this is to pass a Variant of subtype Null. Null is specifically set aside for the purpose of indicating not known.

If the parameter had not been a Variant, you would have had some choices:

If the parameters are Variants, you avoid these unsatisfactory choices when modifying the functions. In the same way, parameters and return types of class methods, as well as properties, should all be declared as Variants instead of first-class data types.

Flexibility and Classes

It is standard to view the interfaces from the classes as a contract in the same way. In this case, the same contract flexibility principles apply.

In effect, you should make only those properties and methods Public that you have to. Avoid the temptation to think, “I’ll also put this stuff in, in case they like it.” It will only turn out to be a burden to you in the future. It is easy to add properties and methods, but it is much harder to take them out. (See also the sidebar entitled “The Shackles of Backward Compatibility” on page 198.)

I have noted that you can and should use Variants instead of integers, strings, and so on, and this is possible because a Variant can be a string or an integer. In the same way, if a class hierarchy exists in your design, you should declare variables to be of the parent class. In other words, declare your parameters and other interface variables to be of type Animal rather than Flea or Tyrannosaurus if it is the case that the Animal interface sufficiently defines the functionality that you utilize within your function.