Search code examples
contractliskov-substitution-principle

Can preconditions be expressed (for verification) as simple conditions?


I understand that a precondition, in context of Desing by contract/Liskov principle, is something that should be true before the code is called, e.g. the caller is responsible for that. Also, the author of the Eiffel language stated that most people do put another verification check into the called cade, simply as means of defensive programming.

Some time ago I read a question with a code similar to this:

    void X(int value)
    {
       if (value > 100)
         {do something...}
    }

Some commenters argued that the if statement is not a precondition but I do not think that is right - if the contract states V must be 100, then this is verifying the precondition additionally and if a class is derived from this type and changes to v > 200, it would be strenghtening the precondition and thus violating the Liskov principle. Or isn't that the case?


Solution

  • As you said, a precondition is defined as a condition that must always be true before the proceeding code executes.

    This means that anything that checks for a condition at the beginning of a function before other code is executed, then it is considered a precondition.

    Example:

    //We will do something cool here
    //With an integer input
    int doSomethingCool(final int input)
    {
        //Wait, what if input is null or less than 100?
        if(null == input || input < 100)
        {
            //Return a -1 to signify an issue
            return -1;    
        }
        //The cool bit of multiplying by 50. So cool.
        final int results = input * 50;
        //Return the results;
        return results;
    }
    

    In the example, the function, input is checked before anything else is executed. So long as the conditions are met, then the rest of the code will execute.

    Bad Example:

    //We will do something cool here
    //With an integer input
    int doSomethingCool(final int input)
    {
        //Want to make sure input is not null and larger than 100
        if(null != input && input > 100)
        {
            //The cool bit of multiplying by 50. So cool.
            final int results = input * 50;
            //Return the results;
            return results;
        }
        //Return a -1 to signify an issue because the
        //preconditions were not met for some reason
        return -1;
    }
    

    In the example, the precondition is to check that input is not null and larger than 100. This is a bad precondition because it could introduce unnecessary nesting of ifs and loops.

    Preconditions should do checks and only return when the checks fail. There should be no work done in a precondition.

    In keeping with the Liskov Substitution Principle, if type S is a subtype of type T, then type T can be replaced with type S. If type S overrides doSomethingCool and changes the precondition, then it is in violation, because type T is the base definition and defines the intended conditions that must be met.

    Now for your answer

    Yes, simple conditions still count as preconditions. As long as they are at the front of all other code that uses the variable, the condition is as what is needed by the program. Also, if the function is in a subtype and is overriding the parent class, then it should not change the precondition.

    However, do not surround the code that you need to run within the precondition. That is bad practice. If value needs to be larger than 100, check value < 100 and set a return in the if check.