Skip to content

Quiz 15 — Answers

Back to the quiz


Q1 — C, Liskov Substitution. A subtype must be usable wherever its base is expected — its behaviour must honour the base's contract. A subtype that throws where the base promises success violates this, even if it compiles.

Q2 — False. Open/Closed means extend the design without editing existing code — add a new class rather than a new branch. It does not forbid fixing bugs or refactoring. The test is: does adding the next feature change an existing class?

Q3 — A, B, C. A class edited for unrelated reasons (A) violates Single Responsibility; a fat interface most implementers leave empty (B) violates Interface Segregation; a domain class writing SQL (C) violates Dependency Inversion. A class with a single clear responsibility (D) is a good smell, not a bad one.

Q4 —

100
90

The Regular policy returns the base price; Member returns 90%. The context does not change — only the policy object does. That is the Strategy pattern, and it satisfies Dependency Inversion: the caller depends on the Pricing abstraction.

Q5 —

coffee + milk
2.5

WithMilk wraps Coffee and adds " + milk" and 0.5. This is the Decorator pattern: behaviour is composed at run time by wrapping, instead of by subclassing every combination.

Q6 — s is a local Summary; returning &s returns the address of an object destroyed at the end of makeReport, so r->render() reads freed storage — undefined behaviour. Fix by returning an owning smart pointer:

std::unique_ptr<Report> makeReport(const std::string& kind) {
    if (kind == "summary") return std::make_unique<Summary>();
    return nullptr;
}

Q7 — It violates the Single Responsibility Principle. Three distinct reasons it changes: the validation rules, the tax rules, the persistence schema (or the receipt format, or the email wording — any three genuine ones). Each is a separate concern that should move to its own class, with OrderService coordinating them.

Q8 — High-level modules should depend on abstractions, not on the concrete details of low-level modules; both should depend on the abstraction. In practice: depend on an interface, and let the implementation be substituted. (In C++, an abstract base class is the abstraction.)

Q9 — Observer interface with one update(event) method. Two safety rules: (1) unsubscription must be safe even if it happens during a notification — for example the subject hands back a token/handle, or defers removals until the loop finishes; and (2) ownership — the subject does not own its observers. The owner is the caller, and an observer must unsubscribe before it is destroyed. Without those rules, a notification can call into a destroyed observer.

Q10 — A sign: a pattern whose only effect is an extra layer with a single implementation and no second variant in sight — a "strategy" with one strategy, a "factory" that always returns the same class. The cost: more types to read, more indirection to follow, and a harder-to-change design where a plain function would have done. Patterns exist to hold variation; if there is no variation, they are decoration.


Marking. One point per question (Q3 all-or-nothing). Then read Marking Yourself.