A base-class change can preserve its own set abstraction while breaking a subclass that intercepts internal calls. For example, suppose
IntegerSet.delete(x) is reimplemented by collecting the elements to retain, calling clear, then calling insert for each retained element. Conceptually its body isval retained = snapshotOfElements.filter(_ != x)
clear
retained.foreach(insert)Here
snapshotOfElements denotes the base implementer's own internal snapshot operation in this hypothetical revision, not an API supplied by the examination. The algorithm still removes exactly the requested value and preserves all other set members.On an
IntegerSetWithCount, however, these unqualified calls use dynamic dispatch. The internal clear resets the subclass counter and the internal insertions increase it. After client calls insert(10), insert(20), insert(10), the correct count is three. Calling delete(10) with this revised base implementation leaves the right set, containing only the other value, but resets the counter and reinserts that value, leaving count one instead of three. The client made no further insertion attempt and requested no clear.This is the fragile base class problem: subclass correctness depends on the base implementation's internal call structure, beyond its public set postconditions. A change need not alter the base API to expose that dependence.
Articles by others on the same topic
There are currently no matching articles.