Skip to main content

Module Quiz

Complete this quiz after finishing all concept and practice pages.

Current Module Questions

Question 1: Cohesion

A class has 12 methods. Seven of them operate on three shared fields; the other five do not use any fields of the class. What is the design concern and what is the smallest corrective move?

Answer: Cohesion is low: the five field-less methods do not belong on this class. Move them to their natural home (static utilities, or onto the class whose data they touch). Five methods that share nothing with the rest are almost always a different responsibility sharing a file.

Question 2: Coupling

Module A iterates B's private list through a public getter and calls .sort() on it. What coupling kind is this, and why is it expensive?

Answer: Content coupling. A now depends on B's internal representation being a mutable, sortable list -- not on a behavior B promises. Any internal change to B's storage breaks A. Pull the operation into B as a narrow method (e.g., B.sortedItems()), so A depends on behavior, not storage.

Question 3: Encapsulation

Every field in User is private and has a getter and setter. Callers regularly read a field, transform it, and write it back. Is User encapsulated? Why or why not?

Answer: No. Syntactic privacy is not encapsulation. The invariant-preserving behavior lives outside the class, so every caller re-implements it. Real encapsulation puts the read-transform-write sequence on User as one named method.

Question 4: Single Responsibility

An Employee class has calculatePay(), saveToDb(), and reportHours(). Why is this an SRP violation, and what is the fix?

Answer: Three distinct stakeholders (HR, DBA, accounting) can each demand independent changes. Each demand touches the same file, creating cross-concern conflict and regression risk. Split into Employee (domain), PayCalculator (HR), EmployeeRepository (DBA), HoursReport (accounting). One reason to change per class.

Question 5: Open-Closed

You introduce a PaymentMethod interface with one implementation, CreditCard, because "we might add PayPal later." Is that appropriate use of OCP? Why or why not?

Answer: No. OCP is a response to real change pressure, not a preemptive design move. With exactly one implementation and no committed second variant, the abstraction is speculative generality. Keep CreditCard concrete; introduce the interface when PayPal is actually scheduled.

Question 6: Liskov Substitution

Explain why Square extends Rectangle violates LSP even though every square is geometrically a rectangle.

Answer: Rectangle's contract includes an implicit postcondition: setWidth(w) leaves height unchanged. Square strengthens the dependency and weakens that postcondition by updating both. Any caller that holds a Rectangle reference and relies on the postcondition can be surprised by a Square. Geometric truth is not behavioral substitutability.

Question 7: Interface Segregation

An interface has print, fax, scan, staple. OldPrinter can only print and throws UnsupportedOperationException from the rest. What does this indicate and how would you fix it?

Answer: ISP violation: OldPrinter is forced to depend on and lie about methods it cannot support. Split by role -- Printable, Faxable, Scannable, Staplable -- and let implementations declare only the roles they actually fulfill.

Question 8: Dependency Inversion

A high-level OrderService imports PostgresOrderDb directly via new. Why is this a DIP violation, and what move fixes it?

Answer: Policy depends on detail; the arrow points from OrderService to Postgres. Introduce an OrderRepository interface owned by the policy package; let PostgresOrderRepository implement it. Both sides now depend on the abstraction; policy can be tested and swapped without knowing which database exists.

Question 9: Names Reveal Intent

Explain why a comment // returns active users above a method named getUsers() is a naming failure rather than a good comment.

Answer: The information the comment carries belongs in the name: renaming to activeUsers() makes the comment unnecessary. A comment that explains what a function does typically signals a missed rename. Keep the comment only when it explains why.

Question 10: Functions -- one thing

A 60-line function has clear sections for "validate," "compute," and "format." The author argues it is one thing because it "produces a formatted result from raw input." Is that argument sound?

Answer: No. The three sections are at different levels of abstraction (validation rules, pure math, and string formatting). Even though the end-to-end purpose is one phrase, the function mixes levels. Extract each section into a named function below; the top-level reads as a plan. "One thing at one level of abstraction" is the full rule.

Question 11: Comments

Why is a misleading comment worse than no comment at all?

Answer: A missing comment forces the reader to read code; a wrong comment convinces them they already understand it. Reviewers trust wrong comments and miss real defects. Either fix the comment or delete it -- never leave a lie.

Question 12: Smell diagnosis -- bloater vs coupler

Distinguish a Long Method from Feature Envy, and give a single concrete case where both apply to the same code.

Answer: Long Method is a size/abstraction-mixing smell. Feature Envy is a cohesion-across-classes smell: the method uses more of another object's data than its own. Both apply when a 60-line method in Invoice spends its lines reading a dozen getters on Customer -- it's long (extract functions) and envious (then move one of those functions onto Customer).

Question 13: Repeated Switch

Why is a single switch in one dispatcher not a Repeated Switch smell, while the same switch appearing in three files is?

Answer: Centralized discrimination in one place has a clear home; it changes as new variants arrive, but only once per variant. Repeating the same switch across N files means every new variant is N edits, any of which can be missed. That repetition -- not switches themselves -- is the smell that justifies Replace Conditional with Polymorphism.

Question 14: Shotgun Surgery

You add a single "preferred language" attribute, and the PR touches seven files. What smell is this, and where is the design missing a home?

Answer: Shotgun Surgery. The change is logically single but physically scattered. It indicates that user-facing presentation concerns do not have a shared module; each consumer reimplements user personalization. The fix is to move scattered behavior into one place (e.g., a UserPresentation service) so future preferences change in one file.

Question 15: YAGNI

You see an abstract base class with three abstract methods, two protected hooks, and exactly one concrete subclass. The author says, "we might add a second subclass later." What should you do, and why?

Answer: Inline until evidence arrives. With one implementation and no committed second variant, the abstraction is pure cost: extra indirection for every reader, an extra surface to test, and a shape that is likely to be wrong for the real second variant when it appears. Re-introduce the abstraction when a second real use case demands it; refactoring later is cheaper than carrying speculative structure.

Interleaved Review Questions

Prior Module Question 1 (S2: Algorithmic Analysis, Big-O)

You replace a switch with polymorphism by storing a Map<Type, Strategy> keyed on a string. The input set is ~5 strategies. What is the asymptotic cost compared to a direct switch, and does that cost matter here?

Answer: Lookup on a small HashMap is O(1) expected, same asymptotic class as a dispatch table, with a slightly higher constant factor (hashing, collision handling). For input sizes of ~5 over typical call rates, the difference is negligible. The design argument (change locality, OCP) wins over the micro-performance argument. If this sat inside a per-packet hot loop, a direct switch or perfect-hash dispatch might be justified -- you measure, you do not guess.

Prior Module Question 2 (S2: Algorithmic Analysis, Design Tradeoffs)

You "encapsulate" a collection by returning List.copyOf(...) everywhere instead of the underlying list. In what scenario is this a bad tradeoff, and how do you decide?

Answer: If callers repeatedly iterate the collection in hot paths, the per-call copy is O(n) extra work and O(n) extra allocation pressure. The design win (no aliasing bugs) may cost measurable latency. Decide by measurement and by API intent: prefer Collections.unmodifiableList or Iterable-returning methods when iteration is the main usage, and reserve copyOf for cases where callers may cache or mutate the result. The principle (encapsulation) is not up for debate; the mechanism to honor it is a tradeoff.

Prior Module Question 3 (S1: Proof Discipline)

You say "every concrete subclass I inspected honors the parent's contract." Is that a valid LSP argument?

Answer: No. That is proof-by-example, which cannot establish a universal claim. Either the contract is specified and mechanically enforced (via tests or type constraints) so new subclasses cannot violate it, or every future subclass is a new risk. LSP is a claim over all subtypes, not over a sample.

Prior Module Question 4 (S1: Conditional Reasoning / Bayes)

You notice that 80% of files flagged as "large class" by a static tool turn out to be fine on review. Explain, in Bayes-style language, why the flag alone is weak evidence.

Answer: P(actual problem | flagged) = P(flagged | problem) * P(problem) / P(flagged). If P(problem) is low (most code is fine) and the flag is noisy (many false positives), the posterior is much lower than the flag suggests. You need additional evidence -- change pressure, review pain, bug history -- to raise the posterior to a point where action is justified. Smells are priors; deciding to refactor requires more signal.

Prior Module Question 5 (S0/S1: Decomposition)

How is "one thing at one level of abstraction" for functions related to "divide and conquer" in algorithms?

Answer: Both are the same move in different settings: break a problem into named subproblems of the same size/level, solve each, and compose. In algorithms you get provable running-time arguments; in software design you get readable top-down code. The habit is identical: name before you solve, stay at one level of abstraction per step.

Self-Assessment and Remediation

Mastery Level (90-100% correct)

  • Status: Ready to advance to Module 2 (Refactoring).
  • Evidence: You can name smells, cite principles, and argue for and against applying moves.
  • Action: Continue with confidence. Maintain the smell-vocabulary deck in spaced repetition.

Proficient Level (75-89% correct)

  • Status: Good understanding; targeted review needed.
  • Evidence: Some confusion between related concepts (e.g., SRP vs cohesion, Divergent Change vs Shotgun Surgery).
  • Action: Re-read the concept pages behind missed questions. Redo the relevant katas (1-3 or 5-6) and add a one-paragraph summary per question you missed.

Developing Level (60-74% correct)

  • Status: Vocabulary present; judgment unstable.
  • Evidence: You can name principles but cannot tell when they apply.
  • Action:
    1. Rework Cluster 1 (cohesion, coupling, encapsulation) until the vocabulary is automatic.
    2. Redo Practice pages 1 and 3 on a fresh codebase.
    3. Write one "when not to" memo per SOLID letter before retaking the quiz.
    4. Retake the quiz with an 80% gate for advancement.

Insufficient Level (<60% correct)

  • Status: Foundational gaps.
  • Evidence: Missing distinction between naming the principle and applying it.
  • Action:
    1. Re-sequence: Cluster 1 -> Cluster 3 -> Cluster 2. Readability and cohesion come before SOLID.
    2. Build the smell-card deck from scratch with one example per card.
    3. Pair-review a real file with another learner and label smells out loud.
    4. Extend the module timeline by one week before retaking the quiz at the Mastery gate.