4

Link

You can think of the class invariant as a health criterion, which must be fulfilled by all objects in between operations. As a precondition of every public operation of the class, it can therefore be assumed that the class invariant holds. In addition, it can be assumed as a postcondition of every public operation that the class invariant holds. In this sense, the class invariant serves as a general strengthening of both the precondition and the postcondition of public operations in the class. The effective precondition is the formulated precondition in conjunction with the class invariant. Similarly, the effective postcondition is the formulated postcondition in conjunction with the class invariant.

public class Server
{
     // other code ommited
    public Output Foo(Input cmdIn)
    {
        ...
        return cmdOut;
    }
}


public class Caller
{  
    // other code ommited

    /* calls Server.Foo */
    public void Call()
    {...}
}

public class Input
{
    // other code ommited

    public int Length
    {...}
}

public class Output
{
    // other code ommited

    public int Length
    {...}
}

1. If class invariant is defined on Server class:

a) Preconditions are typically formulated in terms of the formal parameters of the called operation, so how can class invariant strengthen method's ( Foo's ) preconditions?

b) Postcondition is formulated in terms of called method's return value, so how can class invariant strengthen method's ( Foo's ) postconditions?

2. Can class invariant defined on Caller class in any way strengthen Foo's preconditions or postconditions?

3. If class invariant is defined on Foo's cmdIn parameter:

a) If precondition on Foo states that cmdIn.Length is within range 1-20, but one of class invariants defined on Input states that Input.Length should be within range 2-19, then Foo's precondition was indeed strenghten?

b) Isn't the logic in a) a bit flawed, since if class invariant already states that Input.Length should be within range 2-19, isn't it then an error for Foo to define a precondition which isn't always be true ( cmdIn.Length can't hold values 1 or 20 )

c) But if class invariant defined on Input states that Input.Length should be within range 0-100, then Foo's precondition isn't strengthen?

d) Can class invariants defined on cmdIn also somehow strengthen Foo's postcondition?

4. If class invariant is defined on Foo's return value

a) If postcondition on Foo states that cmdOut.Length is within range 1-20, but one of class invariants defined on Output states that Output.Length should be within range 2-19, then Foo's postcondition was indeed strengthen?

b) But if invariant defined on Output states that Output.Length should be within range 0-100, then Foo's postcondition wasn't strengthen?

c) Can class invariants defined on Output also somehow strengthen Foo's precondition?

5. But I get the impression that quoted article meant to say that just by having a class invariant ( and even if this invariant doesn't in any way operate ( directly or indirectly ) on Foo's parameters and/or return value, it would still strengthen Foo's preconditions and postcondition? If that's what article is actually implying, how is that possible?

thanks

MattDavey
  • 8,897
  • 3
  • 31
  • 54
EdvRusj
  • 745
  • 7
  • 14

1 Answers1

3

a) Preconditions are typically formulated in terms of the formal parameters of the called operation, so how can class invariant strengthen method's ( Foo's ) preconditions?

I suspect that's key to your misunderstanding. Pre-conditions may include formal parameters - but are not restricted to them. They can - and often do - also refer to class features (attributes/operations). In general, the combination of invariants and pre-condition defines a set of constraints that must be satisfied before an operation is obliged to meet its post-condition when called. Similarly, an operation must guarantee that both its post condition and any invariants will be satisfied when it completes. Take the example of a Bounded Buffer:

Class BoundedBuffer<T> {
   public int max    // max #items the buffer can hold
   public int count  // how many items currently in the buffer

   void push(T item) {...}
   T    pop() {...}
}

A pre-condition for push() would be that the buffer has not reached its maximum size:

 pre: count < max

So here the pre-condition doesn't even mention the operation's formal parameter. We can also state an invariant for the Buffer:

inv: count >=0  //can't have -ve number of elements in the buffer

It strengthens the pre-condition because it entends what must be true before the push() operation is obliged to meet its post condition. The two clauses are logically ANDed together. So the effective pre-condition is count >=0 AND count < max. That's a stronger (more restrictive) constraint than the pre-condition alone.

Note the concept isn't restricted to situations where the pre-condition refers to class features. Let's add the constraint that the size of any individual item being added to the buffer must be less than some upper limit:

pre: count < max AND item.size() <= MAX_ITEM_SIZE

Adding the invariant still strengthens the effective pre-condition to become:

pre: count < max AND item.size() <= MAX_ITEM_SIZE AND count >=0

So in summary: invariants must hold before an operation is invoked and after an operation completes. Hence they strengthen both.

  1. Can class invariant defined on Caller class in any way strengthen Foo's preconditions or postconditions?

No. Invariants apply to the class they are defined on only.

Answers to your remaining questions flow logically from above.

hth.

sfinnie
  • 9,854
  • 1
  • 38
  • 44
  • I thought preconditions were a public contract between a caller and a method. In other words, I thought any demands stated by preconditions must be met by caller in a sense that caller has the ability to manipulate all the features stated in pre-condition. But you're saying that caller may not even be aware of some of the features specified by preconditions ( those which it can't/shouldn't manipulate, such as value of private variables etc )? – EdvRusj May 02 '13 at 23:15
  • Forgot to add: "No. Invariants apply to the class they are defined on only." But couldn't we say that example in 3a) does strengthen Foo's precondition, since due to class invariant defined on Input type, the allowed range of Server.Foo's parameter cmdIn.Length is 2-19 instead of 1-20? – EdvRusj May 02 '13 at 23:17
  • @EdvRusj: Preconditions are not solely about the caller's responsibilities. They also provide assumptions the callee is allowed to rely on. – Kevin Dec 09 '14 at 17:43
  • very good answer, there is one thing not clear to me: *invariants must hold before an operation is invoked and after an operation completes*. Why is that? is not it enough to be held only in the postconditions? Afterall, in the preconditions I am sure that the object is in a valid state, given that I have not done anything on it yet. – Marco Luzzara Feb 04 '21 at 09:11