19-Soft-B17 Data Visualization · December 2014
Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)
Question text not reproduced: the examination questions are © Engineers and Geoscientists BC. Open the official past paper (linked at the top of this page) to read the question, then follow the worked solution below.
The Liskov Substitution Principle (LSP) states that if S is declared a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program — correctness, expected behaviour, invariants that client code relies on. Crucially, LSP is a behavioural requirement, not merely a syntactic one: it is not enough for S to inherit or override every method of T with a matching signature (which is all that subclassing in most OO languages actually enforces at compile time); S's methods must also honour every contract — preconditions no stronger, postconditions no weaker, and invariants preserved — that clients of T were entitled to assume. Subclassing therefore gives you subtyping syntactically for free, but it does not automatically give you subtyping behaviourally; the two can diverge whenever the subclass's overridden behaviour surprises code written only against the base class's contract.
Square extends Rectangle syntactically, but substituting a Square wherever client code holds a Rectangle reference breaks a postcondition (independent width/height mutation) that the client is entitled to assume.The canonical counter-example is exactly the one hinted at: a Rectangle class exposes independent setWidth(w) and setHeight(h) mutators, with the implicit invariant/contract that setting one dimension leaves the other unchanged (so area() after setWidth(5); setHeight(4) is always 20, regardless of the order the two setters are called). A Square subclass, to preserve its own geometric invariant that all four sides are equal, must make setWidth also change the height (and vice versa) — there is no other way for a Square instance to stay a valid square after either setter runs. This is a perfectly reasonable design for Square considered in isolation, and Square is syntactically a valid subclass (it compiles, it overrides both methods with matching signatures). But it is not a valid subtype under LSP: any client code written against the Rectangle contract — for instance, code that calls setWidth(5) then setHeight(4) and asserts the resulting area is 20 — will silently get the wrong answer (16) if a Square is substituted for the Rectangle it expected, because setHeight just overwrote the width the client had already set. The subclass has strengthened an invariant (all sides equal) in a way that weakens a postcondition the base class's clients relied on (dimensions set independently), which is exactly the kind of behavioural contract violation LSP forbids. The general lesson is that "is-a" in the everyday sense ("a square is a rectangle," which is true geometrically) does not automatically license "is-a" in the substitutability sense that OO subtyping requires; whether subclassing is safe must be checked against the base class's actual behavioural contract, not against real-world taxonomy.