| name | abstraction |
| description | Abstraction and LSP |
Barbara Liskov Principles
Applying Barbara Liskov's foundational work on data abstraction and behavioral subtyping from CLU, Argus, and the Liskov Substitution Principle. The "L" in SOLID.
Core Philosophy
The Liskov Substitution Principle
"If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T."
In plain terms: Subtypes must be substitutable for their base types without breaking program correctness.
class Rectangle {
protected int width, height;
public void setWidth(int w) { width = w; }
public void setHeight(int h) { height = h; }
public int area() { return width * height; }
}
class Square extends Rectangle {
@Override
public void setWidth(int w) { width = height = w; }
@Override
public void setHeight(int h) { width = height = h; }
}
void testRectangle(Rectangle r) {
r.setWidth(5);
r.setHeight(4);
assert r.area() == 20;
}
interface Shape {
int area();
}
class Rectangle implements Shape {
private final int width, height;
public Rectangle(int w, int h) { width = w; height = h; }
public int area() { return width * height; }
}
class Square implements Shape {
private final int side;
public Square(int s) { side = s; }
public int area() { return side * side; }
}
Behavioral Subtyping Rules
A subtype must satisfy:
- Signature Rule: Method signatures compatible (covariant returns, contravariant parameters)
- Methods Rule: Subtype methods preserve supertype behavior
- Properties Rule: Subtype preserves supertype invariants
interface Stack<T> {
void push(T item);
T pop();
boolean isEmpty();
int size();
}
class BoundedStack<T> implements Stack<T> {
}
Data Abstraction
Abstract Data Types (ADTs)
From CLU: Define types by their operations, not their representation.
class Set:
"""
Abstract Data Type: Set of unique elements
Operations:
add(element) -> None # Adds element if not present
remove(element) -> None # Removes element if present
contains(element) -> bool # True if element in set
size() -> int # Number of elements
Invariants:
- No duplicates
- size() >= 0
- add(x) then contains(x) == True
- remove(x) then contains(x) == False
"""
def __init__(self):
self._elements = []
def add(self, element):
if element not in self._elements:
self._elements.append(element)
Information Hiding
The representation is PRIVATE. Only operations are PUBLIC.
public class Account {
public double balance;
}
public class Account {
private double balance;
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException();
balance += amount;
}
public void withdraw(double amount) {
if (amount <= 0 || amount > balance)
throw new IllegalArgumentException();
balance -= amount;
}
public double getBalance() { return balance; }
}
Specification & Contracts
Pre/Post Conditions
Every operation has a contract:
public int indexOf(int[] arr, int target) {
if (arr == null) throw new NullPointerException();
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) return i;
}
return -1;
}
Class Invariants
Properties that must ALWAYS be true (between method calls):
public class SortedList<T extends Comparable<T>> {
private List<T> elements = new ArrayList<>();
public void add(T item) {
int pos = Collections.binarySearch(elements, item);
if (pos < 0) pos = -(pos + 1);
elements.add(pos, item);
}
public T get(int index) {
return elements.get(index);
}
public List<T> getElements() {
return new ArrayList<>(elements);
}
}
Inheritance Guidelines
When to Use Inheritance
YES: True behavioral subtyping
class FileInputStream extends InputStream {
}
NO: Implementation reuse without behavioral compatibility
class Stack extends Vector { }
class Stack<T> {
private List<T> items = new ArrayList<>();
public void push(T item) { items.add(item); }
public T pop() { return items.remove(items.size() - 1); }
}
The Liskov Test
Before creating class S extends T, ask:
- Can S be used everywhere T is expected?
- Does S preserve all of T's invariants?
- Does S honor all of T's method contracts?
- Would a client using T be surprised by S's behavior?
If ANY answer is "no" or "maybe", don't inherit.
CLU Innovations (Historical Context)
Liskov's CLU language (1974-1977) pioneered:
| CLU Feature | Modern Equivalent |
|---|
| Clusters (ADTs) | Classes with private fields |
| Iterators | Python generators, Java Iterators |
| Exception handling | try/catch/throw |
| Parameterized types | Generics/Templates |
| Multiple return values | Tuples, destructuring |
% CLU cluster (abstract data type)
int_set = cluster is create, insert, member, size
rep = array[int]
create = proc() returns (cvt)
return (rep$new())
end create
insert = proc(s: cvt, i: int)
if ~member(up(s), i) then rep$addh(s, i) end
end insert
member = proc(s: cvt, i: int) returns (bool)
for x: int in rep$elements(s) do
if x = i then return (true) end
end
return (false)
end member
end int_set
Review Checklist
When reviewing type hierarchies:
Substitutability
Abstraction Quality
Contract Clarity
When to Apply
| Scenario | Apply Liskov |
|---|
| Designing inheritance hierarchy | Yes - LSP is mandatory |
| Defining interfaces/contracts | Yes - specify behavior |
| Reviewing OO design | Yes - check substitutability |
| Creating ADTs | Yes - information hiding |
| Performance optimization | No - see optimization |
| Implementation patterns | Partially - see design-patterns |
Source Material
- "A Behavioral Notion of Subtyping" (1994) - Liskov & Wing
- "Data Abstraction and Hierarchy" (1987) - OOPSLA keynote
- "Abstraction and Specification in Program Development" (1986) - Liskov & Guttag
- "CLU Reference Manual" (1981)
- Turing Award Lecture (2008)