Invariants for Non-Hierarchical Object Structures
Ronald Middelkoop, Kees Huizing, Ruurd Kuiper and
Erik Luit
Ronald Middelkoop
Overview
• Setting: Sequential Object-Oriented Development
• Classical Local Invariants
• Advantages of invariants
• Non-local Invariants
– Methods that re-establish an invariant
– Information Hiding
• Conclusions
2
Ronald Middelkoop
Classical Local Invariants
Class C
int i;
Visible state: pre- or post-state of a
method execution
inv this.i > 2;
C() {…}
int getI() {…}
void setI(int newI) {…}
Visible state semantics:
After an object’s creation, its invariant I
holds in every visible state
3
Ronald Middelkoop
Classical Local Invariants
Class C
int i;
Visible state: pre- or post-state of a
method execution
inv this.i > 2;
C() {…}
int getI() {…}
void setI(int newI) {…}
Theorem:
After an object’s creation, its invariant I
holds in every visible state, when
• The constructor of that object
establishes I
• All methods in the program preserve I
• No method is called while I is invalid
4
Ronald Middelkoop
Classical Local Invariants
Class C
int i;
Visible state: pre- or post-state of a
method execution
inv this.i > 2;
C() {
pre:
true;
post: true;
impl: this.i = 4;
}
int getI() {…}
void setI(int newI) {…}
Theorem:
After an object’s creation, its invariant I
holds in every visible state, when
• The constructor of that object
establishes I
• All methods in the program preserve I
• No method is called while I is invalid
5
Ronald Middelkoop
Classical Local Invariants
Class C
int i;
Visible state: pre- or post-state of a
method execution
inv this.i > 2;
C() {…}
int getI() {
pre:
true;
post: result == this.i;
impl: return this.i;
}
void setI(int newI) {…}
Theorem:
After an object’s creation, its invariant I holds
in every visible state, when
• The constructor of that object
establishes I
• All methods in the program preserve I
• No method is called while I is invalid
6
Ronald Middelkoop
Classical Local Invariants
Class C
int i;
Visible state: pre- or post-state of a
method execution
inv this.i > 2;
C() {…}
int getI() {
pre:
true;
post: result == this.i;
impl: return this.i;
}
void setI(int newI) {
pre:
newI > 2;
post: this.i == newI;
impl: this.i = newI;
Theorem:
After an object’s creation, its invariant I holds
in every visible state, when
• The constructor of that object
establishes I
• All methods in the program preserve I
• No method is called while I is invalid
}
7
Ronald Middelkoop
Advantages of Invariants
Class C
int i;
inv this.i > 2;
C() {…}
int getI() {…}
void setI(int newI) {…}
int m() {
pre:
true;
post: result > 4;
impl: return this.i +2;
}
8
Ronald Middelkoop
Advantages of Invariants
Class C
int i;
C() {…}
int getI() {…}
void setI(int newI) {…}
int m() {
pre:
true;
post: result > 4;
impl: return this.i +2;
}
m relies on i > 2
9
Ronald Middelkoop
Advantages of Invariants
Class C
int i;
C() {…}
int getI() {…}
void setI(int newI) {…}
int m() {
pre:
this.i > 2;
post: result > 4;
impl: return this.i +2;
}
m relies on i > 2 m requires i > 2
10
Ronald Middelkoop
Advantages of Invariants
Class C
Class D
int i;
C c;
C() {…}
inv this.c != null;
int getI() {…}
int useC() {
pre:
true;
post: result > 4;
impl: return this.c.getI() + 2;
}
void setI(int newI) {…}
int m() {
pre:
this.i > 2;
post: result > 4;
impl: return this.i +2;
}
useC relies on c.i > 2
11
Ronald Middelkoop
Advantages of Invariants
Class C
Class D
int i;
C c;
C() {…}
inv this.c != null;
int getI() {…}
int useC() {
pre:
this.c.i > 2;
post: result > 4;
impl: return this.c.getI() + 2;
}
void setI(int newI) {…}
int m() {
pre:
this.i > 2;
post: result > 4;
impl: return this.i +2;
}
useC relies on c.i > 2 useC requires c.i > 2
12
Ronald Middelkoop
Advantages of Invariants
Class D
Class E
C c;
D d;
inv this.c != null;
int useUseC() {
pre:
this.d != null;
post: result > 8;
impl: return this.d.useC() + 4;
}
int useC() {
pre:
this.c.i > 2;
post: result > 4;
impl: return this.c.getI() + 2;
}
useUseC calls d.useC() useC requires c.i > 2
useUseC has to establish d.c.i > 2
13
Ronald Middelkoop
Advantages of Invariants
Class D
Class E
C c;
D d;
inv this.c != null;
int useUseC() {
pre:
this.d != null this.d.c.i > 2;
post: result > 8;
impl: return this.d.useC() + 4;
}
int useC() {
pre:
this.c.i > 2;
post: result > 4;
impl: return this.c.getI() + 2;
}
useUseC calls d.useC() useC requires c.i > 2
useUseC has to establish d.c.i > 2 useUseC requires d.c.i > 2
14
Ronald Middelkoop
Advantages of Invariants
Class D
Class E
C c;
D d;
inv this.c != null;
int useUseC() {
pre:
this.d != null this.d.c.i > 2;
post: result > 8;
impl: return this.d.useC() + 4;
}
int useC() {
pre:
this.c.i > 2;
post: result > 4;
impl: return this.c.getI() + 2;
}
The invariant property propagates throughout the specification
15
Ronald Middelkoop
Advantages of Invariants
Class D
Class E
C c;
D d;
inv this.c != null;
int useUseC() {
pre:
this.d != null this.d.c.i > 2;
post: result > 8;
impl: return this.d.useC() + 4;
}
int useC() {
pre:
this.c.i > 2;
post: result > 4;
impl: return this.c.getI() + 2;
}
Advantage 1: Significant reduction in specification overhead
Advantage 2: Re-implementations of a method can rely on a different set of invariants
16
Ronald Middelkoop
Advantages of Invariants
Sufficient:
The verifier of a method M that relies on an invariant I can
– Assume that I holds when M is called
– Deduce if a method M’ that is called by M preserves I
Advantage 1: Significant reduction in specification overhead
Advantage 2: Re-implementations of a method can rely on a different set of invariants
17
Ronald Middelkoop
Non-local Invariants
Class CSubject
Class CObserver
int d;
CSubject cs;
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
}
inv this.cs != null;
void getD() {…}
CSubject1
CObserver(CSubject toObs) {…}
int getVal() {
pre:
true;
post: return == f(this.cs.d);
impl: int i = this.cs.getD();
return f(i); //pseudocode
}
CObserver1
18
Ronald Middelkoop
Non-local Invariants
Class CSubject
Class CObserver
int d;
CSubject cs;
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
}
int i;
void getD() {…}
CSubject1
inv this.cs != null;
CObserver(CSubject toObs) {…}
int getVal() {
pre:
true;
post: return == f(this.cs.d);
impl: return this.i;
}
CObserver1
19
Ronald Middelkoop
Non-local Invariants
Class CSubject
Class CObserver
int d;
CSubject cs;
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
}
int i;
void getD() {…}
• The constructor establishes I
• All methods in the program preserve I
• No method is called while I is invalid
inv this.i == f(this.cs.d);
inv this.cs != null;
CObserver(CSubject toObs) {…}
int getVal() {
pre:
true;
post: return == f(this.cs.d);
impl: return this.i;
}
CObserver X • X.cs ≠ this “invariant of X still holds after assignment”
20
Ronald Middelkoop
Non-local Invariants
Class CSubject
Class CObserver
int d;
CSubject cs;
CObserver co;
int i;
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
}
inv this.i == f(this.cs.d);
void getD() {…}
• The constructor establishes I
• All methods in the program preserve I
• No method is called while I is invalid
inv this.cs
this == !=
this.cs.co;
null;
CObserver(CSubject toObs) {…}
int getVal() {
pre:
true;
post: return == f(this.cs.d);
impl: return this.i;
}
void update() {
post: this.i == f(this.cs.d);
CObserver X • X.cs ≠ this “invariant of X still holds after assignment”
}
CObserver X • X.cs = this this.co = X
21
Ronald Middelkoop
Non-local Invariants
Class CSubject
Class CObserver
int d;
CSubject cs;
CObserver co;
int i;
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
if (this.co != null) {
this.co.update();
}
}
inv this.i == f(this.cs.d);
void getD() {…}
• The constructor establishes I
• All methods in the program preserve I
• No method is called while I is invalid
inv this == this.cs.co;
CObserver(CSubject toObs) {…}
int getVal() {
pre:
true;
post: return == f(this.cs.d);
impl: return this.i;
}
void update() {
post: this.i == f(this.cs.d);
}
22
Ronald Middelkoop
Non-local Invariants
Class CSubject
Class CObserver
int d;
CSubject cs;
CObserver co;
int i;
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
if (this.co != null) {
this.co.update();
}
}
inv this.i == f(this.cs.d);
void getD() {…}
• The constructor establishes I
• All methods in the program preserve I
• No method is called while I is invalid
inv this == this.cs.co;
CObserver(CSubject toObs) {…}
int getVal() {
pre:
true;
post: return == f(this.cs.d);
impl: return this.i;
}
void update() {
inc:
this;
post: this.i == f(this.cs.d);
}
23
Ronald Middelkoop
Non-local Invariants with inc
Theorem:
If, for any invariant I of any object,
– The constructor of that object establishes I
– All methods in the program preserve I
– While I is invalid, any
no method
methodisthat
called
is called specifies it doesn’t rely on I
Then, for any method M, for any invariant I of any allocated object,
– Unless M is the constructor of that object,
object I holds
or specifies
when M isthat
called
it
rely on
I
, I holds
whenbyMMisterminates
called
– doesn’t
I holds when
a method
M’ called
– IWhen
holdsI when
holds awhen
method
a method
M’ called
M’ by
is called
M terminates
by M, I holds when M’
terminates
24
Ronald Middelkoop
Non-local Invariants with inc
Class CSubject
Class CObserver
int d;
CSubject cs;
CObserver co;
int i;
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
if (this.co != null) {
this.co.update();
}
}
inv this.i == f(this.cs.d);
void getD() {…}
inv this == this.cs.co;
CObserver(CSubject toObs) {…}
int getVal() {…}
void update() {
inc:
this;
post: this.i == f(this.cs.d);
}
25
Ronald Middelkoop
Non-local Invariants with inc
Class CSubject
Class CObserver
int d;
CSubject cs;
CObserver co;
int i;
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
if (this.co != null) {
this.co.update();
}
}
inv this.i == f(this.cs.d);
void getD() {…}
inv this == this.cs.co;
CObserver(CSubject toObs) {…}
int getVal() {…}
void update() {
inc:
this;
post: this.i == f(this.cs.d);
impl: int d = this.cs.getD();
this.i = f(d);
}
• Update relies on this.cs ≠ null, which follows from this = this.cs.co
26
Ronald Middelkoop
Non-local Invariants with inc
Class CSubject
Class CObserver
int d;
CSubject cs;
CObserver co;
int i;
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
if (this.co != null) {
this.co.update();
}
}
inv I def this.i == f(this.cs.d);
void getD() {…}
inv J def this == this.cs.co;
CObserver(CSubject toObs) {…}
int getVal() {…}
void update() {
inc:
this;
post: this.i == f(this.cs.d);
impl: int d = this.cs.getD();
this.i = f(d);
}
• Update relies on this.cs ≠ null, which follows from this = this.cs.co
27
Ronald Middelkoop
Non-local Invariants with inc
Class CSubject
Class CObserver
int d;
CSubject cs;
CObserver co;
int i;
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
if (this.co != null) {
this.co.update();
}
}
inv I def this.i == f(this.cs.d);
void getD() {…}
inv J def this == this.cs.co;
CObserver(CSubject toObs) {…}
int getVal() {…}
void update() {
inc:
I(this);
post: this.i == f(this.cs.d);
impl: int d = this.cs.getD();
this.i = f(d);
}
• While
UpdateI is
relies
invalid,
on this.cs
any method
≠ null,that
which
is called
followsspecifies
from thisit =doesn’t
this.cs.co
rely on I
28
Ronald Middelkoop
Non-local Invariants with inc
Class CSubject
Class CObserver
int d;
CSubject cs;
CObserver co;
int i;
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
if (this.co != null) {
this.co.update();
}
}
inv I def this.i == f(this.cs.d);
inv J def this == this.cs.co;
CObserver(CSubject toObs) {…}
int getVal() {…}
void update() {
inc:
I(this);
post: this.i == f(this.cs.d);
impl: int d = this.cs.getD();
this.i = f(d);
}
void getD() {
inc:
I(this.co);
impl: return this.d;
}
• While I is invalid, any method that is called specifies it doesn’t rely on I
29
Ronald Middelkoop
Non-local Invariants with inc
Class CSubject
Class CObserver
int d;
CSubject cs;
ONode on;
int i;
void setD(int newD) { …}
inv I def this.i == f(this.cs.d);
void getD() {…}
CObserver(CSubject toObs) {…}
int getVal() {…}
void update() {…}
Class ONode
CObserver obs;
ONode next;
inv this.obs != null;
30
Ronald Middelkoop
Non-local Invariants with inc
Class CSubject
Class CObserver
int d;
CSubject cs;
ONode on;
int i;
void setD(int newD) {
this.d = newD; Onode iter = on;
while (iter != null) {
iter.obs.update(); iter = iter.next;
}
}
inv I def this.i == f(this.cs.d);
void getD() {…}
CObserver(CSubject toObs) {…}
int getVal() {…}
void update() {…}
Class ONode
CObserver obs;
ONode next;
inv this.obs != null;
31
Ronald Middelkoop
Non-local Invariants with inc
Class CSubject
Class CObserver
int d;
CSubject cs;
ONode on;
int i;
void setD(int newD) {
this.d = newD; Onode iter = on;
while (iter != null) {
iter.obs.update(); iter = iter.next;
}
}
inv I def this.i == f(this.cs.d);
void getD() {…}
inv J def i • this == this.cs.on.nexti.obs;
CObserver(CSubject toObs) {…}
int getVal() {…}
void update() {…}
Class ONode
CObserver obs;
ONode next;
inv this.obs != null;
32
Ronald Middelkoop
Non-local Invariants with inc
Class CSubject
Class CObserver
int d;
CSubject cs;
ONode on;
int i;
void setD(int newD) {
this.d = newD; Onode iter = on;
while (iter != null) {
iter.obs.update(); iter = iter.next;
}
}
inv I def this.i == f(this.cs.d);
void getD() {…}
Class ONode
CObserver obs;
ONode next;
inv this.obs != null;
inv J def i • this == this.cs.on.nexti.obs;
CObserver(CSubject toObs) {…}
int getVal() {…}
void update() {
inc:
I(this)
(CObserver,
I, true)
this == ==
this.cs
inc)
inc.cs)
post: this.i == f(this.cs.d);
}
void M(p_1, …,p_n) {
inc: (C, I, P), (C’, I’, P’), …
}
33
Ronald Middelkoop
Information Hiding
Want Subject to work with different kinds of Observers
(simultaneously)
Concrete Observer(s) should be hidden from Concrete
Subject
Then: developing new observers doesn’t affect the
implementation, specification or verification of the
Concrete Subject.
34
Ronald Middelkoop
Information Hiding
Class CSubject
Class CObserver
int d;
CSubject cs;
CObserver co;
int i;
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
if (this.co != null) {
this.co.update();
}
}
inv I def this.i == f(this.cs.d);
void getD() {…}
inv J def this == this.cs.co;
CObserver(CSubject toObs) {…}
int getVal() {…}
void update() {
inc:
I(this);
post: this.i == f(this.cs.d);
impl: int d = this.cs.getD();
this.i = f(d);
}
Invariant I of CObserver not visible to CSubject
35
Ronald Middelkoop
Information Hiding
Class CSubject
Class CObserver
int d coop I(this.co);
CSubject cs;
CObserver co;
int i;
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
if (this.co != null) {
this.co.update();
}
}
inv I def this.i == f(this.cs.d);
void getD() {…}
inv J def this == this.cs.co;
CObserver(CSubject toObs) {…}
int getVal() {…}
void update() {
inc:
I(this);
post: this.i == f(this.cs.d);
impl: int d = this.cs.getD();
this.i = f(d);
}
Invariant I of CObserver not visible to CSubject
36
Ronald Middelkoop
Information Hiding
Class CSubject
Class CObserver
int d coop I(this.co);
CSubject cs coop I(this), J(this);
CObserver co coop I(this.co), J(this.co);
int i coop I(this);
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
if (this.co != null) {
this.co.update();
}
}
inv I def this.i == f(this.cs.d);
void getD() {…}
inv J def this == this.cs.co;
CObserver(CSubject toObs) {…}
int getVal() {…}
void update() {
inc:
I(this);
post: this.i == f(this.cs.d);
impl: int d = this.cs.getD();
this.i = f(d);
}
Invariant I of CObserver not visible to CSubject
37
Ronald Middelkoop
Information Hiding
Class CSubject
Class CObserver
int d coop I(this.co);
CSubject cs coop I(this), J(this);
CObserver co coop I(this.co), J(this.co);
int i coop I(this);
void setD(int newD) {
pre:
true;
post: this.d == newD;
impl: this.d = newD;
if (this.co != null) {
this.co.update();
}
}
inv I def this.i == f(this.cs.d);
void getD() {…}
inv J def this == this.cs.co;
CObserver(CSubject toObs) {…}
int getVal() {…}
void update() {
inc:
I(this);
post: I(this);
impl: int d = this.cs.getD();
this.i = f(d);
}
Invariant I of CObserver not visible to CSubject
38
Ronald Middelkoop
Information Hiding
Class Subject
Interface Observer
Observer o coop I(this.o), J(this.o);
abstract Subject s coop J(this);
…
abstract inv I;
inv J def this == this.s.o;
Class CSubject extends Subject
int d coop I(this.o);
void setD(int newD) {
post: this.d == newD;
impl: this.d = newD;
if (this.o != null) {
this.o.update();
}
}
abstract void update() {
inc:
I(this);
post: I(this);
}
Class CObserver implements
Observer
…
39
Ronald Middelkoop
Information Hiding
Interface Observer
abstract Subject s coop J(this);
Class CObserver implements
Observer
abstract inv I;
int i coop I(this);
inv J def this == this.s.o;
CSubject cs coop I(this);
abstract void update() {
inc:
I(this);
post: I(this);
}
def s by cs;
def I by this.i == f(this.cs.d) J(this);
CObserver(CSubject toObs) {…}
void update() {
impl: int d = this.cs.getD();
this.i = f(d);
}
40
Ronald Middelkoop
Conclusions
• inc allows one to call methods that re-establish
invariants
• coop allows one to make explicit which invariants
might be invalidated when a field is assigned to
• Together, they allow the full, modular specification of
complex, non-hierarchical designs like the Observer
Pattern
Future Work:
• Complement with other solutions, in particular
hierarchical ones
• Combine with other ways of making dependencies
explicit
41
Ronald Middelkoop
Proof Obligations
M() { … {P0} r.f
:= E; {P1} S2; {P2}
if (b) {
{P3} S3; {P4}
} else {
{P6} S4; {P7}
int f coop (C,I,P)
int f coop I(r)
int f coop (C, I, dep = r)
} {P8}
while (b) {
{P9} S5; {P10}
} {P11}
}
50
Ronald Middelkoop
Proof Obligations
M() { …
{P0’ (X = null P[X/dep])} r.f := E; {P1} S2;
{P2}
if (b) {
{P3} S3; {P4}
} else {
int f coop (C,I,P)
int f coop I(r)
int f coop (C, I, dep = r)
{P6} S4; {P7}
} {P8}
while (b) {
{P9} S5; {P10}
}
} {P11}
X represents an arbitrary C-object that the assignment might make inconsistent
51
Ronald Middelkoop
Proof Obligations
M() { …
{P0’ (X = null P[X/dep])} r.f := E; {P1} S2; {P2}
if (b) {
{P3} S3; {P4}
} else {
{P6} S4; {P7}
int f coop (C,I,P)
int f coop I(r)
int f coop (C, I, dep = r)
} {P8}
while (b) {
? P1 I(X) ?
{P9} S5; {P10}
} {P11}
}
52
Ronald Middelkoop
Proof Obligations
M() { …
{P0’ (X = null P[X/dep])} r.f := E; {P1} S2; {P2}
if (b) {
{P3} S3; {P4}
} else {
{P6} S4; {P7}
int f coop (C,I,P)
int f coop I(r)
int f coop (C, I, dep = r)
} {P8}
while (b) {
? S2 Method call ?
{P9} S5; {P10}
} {P11}
}
53
Ronald Middelkoop
Proof Obligations
M() { …
{P0’ (X = null P[X/dep])} r.f := E; {P1} S2; {P2}
if (b) {
{P3} S3; {P4}
} else {
{P6} S4; {P7}
int f coop (C,I,P)
int f coop I(r)
int f coop (C, I, dep = r)
} {P8}
while (b) {
? P2 I(X) ?
{P9} S5; {P10}
} {P11}
}
54
Ronald Middelkoop
Proof Obligations
M() { …
{P0’ (X = null P[X/dep])} r.f := E; {P1} S2; {P2}
if (b) {
int f coop (C,I,P)
{P3} S3; {P4}
int f coop I(r)
} else {
int f coop (C, I, dep = r)
{P6} S4; {P7}
} {P8}
Don’t know which branch is taken;
Branches might contain calls
while (b) {
{P9} S5; {P10}
} {P11}
}
55
Ronald Middelkoop
Proof Obligations
M() { …
{P0’ (X = null P[X/dep])} r.f := E; {P1} S2; {P2}
if (b) {
{P3} S3; {P4}
} else {
{P6} S4; {P7}
} {P8}
while (b) {
int f coop (C,I,P)
int f coop I(r)
int f coop (C, I, dep = r)
Don’t know if while is executed;
Body might contain calls
{P9} S5; {P10}
} {P11}
}
56
© Copyright 2026 Paperzz