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.
Object composition builds new functionality by having one object hold a reference to (and delegate work to) one or more other objects — a "has-a" relationship — while inheritance builds new functionality by having a subclass extend a superclass and acquire its interface and implementation automatically — an "is-a" relationship. Composition is the preferable choice in several recurring situations. First, whenever the relationship between two concepts is genuinely "has-a" rather than "is-a": a Car has an Engine, it is not a kind of Engine, so Car should hold an Engine reference rather than extend an Engine class. Using inheritance for a has-a relationship exposes the whole of the "parent's" interface on the child even when most of it is nonsensical for the child, and is a well-known design smell.
Second, composition is preferable when the goal is code reuse without contractual commitment: inheritance forces the subclass to honour the full behavioural contract of the superclass (per the Liskov Substitution Principle discussed above), including contracts that were never intended to be exposed to the subclass's own clients. Composition lets an object reuse another object's implementation internally while presenting its own, independently-designed public interface — the reused object is an implementation detail, not a public commitment. Third, composition avoids the fragile base class problem: because a subclass's implementation is often coupled to internal details of its superclass (through protected members, or through overriding methods the superclass itself calls internally), a seemingly safe change to the superclass can silently break every subclass. An object obtained through composition, by contrast, is used only through its public interface, so it can be changed or even replaced by a different implementation of the same interface without touching the composing class.
Fourth, composition supports run-time flexibility that static inheritance cannot: the composed object (or its interface) can be swapped out while the program is running — this is exactly the mechanism behind the Strategy design pattern, where a class delegates an algorithm to a composed "strategy" object and can be reconfigured with a different strategy at run time, something a compile-time inheritance relationship cannot do. Fifth, composition sidesteps the complications of deep or multiple inheritance hierarchies — diamond-inheritance ambiguities, deep hierarchies that are hard to understand and change, and the temptation to inherit from several classes purely to reuse unrelated pieces of behaviour. The long-standing design guideline "favour composition over inheritance" captures this: inheritance should be reserved for cases where a genuine, LSP-respecting is-a subtyping relationship exists and is expected to remain stable, while composition is the default, lower-risk tool for reusing behaviour in every other case.