- Which reorganization most clearly represents the supported capabilities without reducing cohesion?
Answer: Keep the common document abstraction focused on rendering and model signing and encryption as separate narrow capabilities implemented only where supported.
When a question asks you to reorganize class hierarchies around "supported capabilities," you're being tested on Interface Segregation and cohesion — core object-oriented design principles. Ask yourself: should a class be forced to declare methods it can never meaningfully implement?
The cleanest solution is C. By keeping Document focused solely on rendering (the one truly universal operation), then modeling signing and encryption as separate, narrow capabilities (typically interfaces like Signable and Encryptable), you ensure that each class only advertises what it actually does. A SecureLegalDocument would implement both interfaces, while a plain Document implements neither. No class carries dead weight, and capabilities compose naturally without forcing awkward inheritance trees.
Option A is a silent failure trap. Replacing UnsupportedOperationException with no-ops means callers receive no feedback that an operation did nothing — bugs become invisible. This is arguably worse than the original design because it hides problems instead of surfacing them.
Option B leads to a combinatorial explosion problem. If you branch on signing and branch again on encryption, you need separate subclasses for every combination (e.g., SignableDocument, EncryptableDocument, SignableEncryptableDocument). This duplicates logic and breaks down as new capabilities are added.
Option D violates cohesion entirely. Centralizing document operations in a utility class that performs instanceof checks is a classic anti-pattern — it scatters behavior away from the data it operates on and makes adding new document types fragile and error-prone.
Study tip: When you see classes throwing UnsupportedOperationException, that's a design smell signaling violated Interface Segregation — the fix is almost always to split capabilities into smaller, focused interfaces rather than inflating a single abstraction.
- Which class organization is most appropriate?
Answer: Keep contact behavior in CustomerProfile and move password verification and lockout state into a separate authentication-focused class associated with the customer.
When a question describes a class whose methods are maintained by different teams and change in separate releases, you're being tested on the Single Responsibility Principle (SRP). SRP says a class should have only one reason to change. The key signal here is that profile-display rules and authentication rules evolve independently — that's two distinct reasons to change, which means one class is doing too much.
Option A is correct because it splits responsibilities along their natural boundary: contact behavior (displaying names, normalizing phone numbers) stays in CustomerProfile, while authentication behavior (verifyPassword, recordFailedLogin, isLockedOut) moves into a dedicated class. Now each class has exactly one reason to change, and the two teams can work independently without risking unintended side effects in each other's code.
Option B is the most common trap. It conflates domain ownership ("same customer") with responsibility. Just because data belongs to the same entity doesn't mean all behavior around it belongs in one class. A customer can have both a name and a password without those concerns being the same responsibility.
Option C introduces an anemic domain model — stripping all behavior into a static utility class leaves CustomerProfile as a hollow data bag. This actually makes the design harder to maintain and test, and it violates object-oriented principles by separating data from its natural behavior without a meaningful benefit.
Option D misuses inheritance. Subclassing to isolate unrelated concerns creates a fragile hierarchy — AuthenticatedCustomerProfile would inherit contact logic it doesn't need to change, coupling the two teams' work rather than separating it.
Study tip: When you see "different teams" or "changes independently," immediately think SRP and look for the answer that creates separate classes with clean boundaries.
- Where should the shipping-cost logic be organized?
Answer: Move it into a cohesive ShippingPolicy that owns the rate table and accepts the required shipping facts from the order.
When you see a question about where to place a method, you're being tested on cohesion and the Single Responsibility Principle: code should live where its primary dependencies and reasons to change align.
The shipping-cost logic depends on two distinct things — order facts (destination, weight, expedited flag) and rate tables that change on their own schedule driven by market conditions. Because the rate table is a core policy concept that evolves independently, the natural home is a dedicated ShippingPolicy class that owns the rate table and accepts only the relevant facts it needs. This is answer B, and it's correct because the class's single reason to change matches the logic's actual reason to change: market pricing rules.
A is tempting because the method reads order properties, but data access alone doesn't determine ownership. Order represents a business transaction entity; embedding market-rate logic there would give Order a reason to change whenever pricing rules are revised — a classic cohesion violation.
C makes the same mistake at a finer granularity. The destination address is just one input signal; it has no business knowing about shipping rates or weight-based pricing tiers. Putting rate logic there would make the address class responsible for concerns far outside its purpose.
D confuses technical convenience with design correctness. The absence of instance fields might make a method a valid static candidate mechanically, but it says nothing about whether OrderService is the right conceptual home for the logic.
As a study pattern: always ask "what causes this code to change, and does that cause align with the class's core responsibility?" That question cuts through most placement distractors.
- Which revision would most improve method organization without returning to one monolithic method?
Answer: Inline mechanical wrappers and extract methods around business concepts such as taxable subtotal, applicable exemptions, and jurisdiction rate.
When a method is over-decomposed into trivial one-line helpers, the real problem isn't the number of methods — it's that the decomposition follows mechanical lines (arithmetic operations) rather than conceptual lines (business rules). Good method organization should make the code's intent readable, not just its mechanics.
Answer A is correct because it targets both problems simultaneously. Inlining wrappers like addAmounts or multiplyRate removes noise — these don't express meaningful concepts, so they shouldn't exist as separate methods. Replacing them with extractions around real business ideas (taxable subtotal, exemptions, jurisdiction rate) means each method now answers the question "what business decision does this represent?" rather than "what arithmetic does this perform?" The result is a calculateTax method whose helper calls read like a policy summary.
B is wrong because renaming helpers with numbered prefixes addresses readability superficially. It sequences the helpers, but the fundamental mismatch between method boundaries and business concepts remains — you still can't quickly locate where jurisdiction rules or exemptions are handled.
C is wrong because making helpers public static doesn't fix the conceptual disorganization at all. It actually worsens encapsulation by exposing internal arithmetic operations to external clients, creating unnecessary coupling to implementation details.
D is wrong because collapsing all helpers into one large arithmetic method just reconstructs the monolithic problem the passage warns against — you'd trade too many small methods for one bloated one, losing clarity in the opposite direction.
Your study tip: when evaluating method decomposition questions, ask whether each method boundary reflects a business concept or just an implementation detail. Java design favors the former.
- Which reorganization best addresses both readability and cohesion rather than merely suppressing the concurrency symptom?
Answer: Keep invocation data local, pass or return explicit step results, and use a per-import context object only if those values naturally belong together.
When a question asks about readability and cohesion — not just fixing a bug — you should think beyond the immediate symptom. The real question is: does the design communicate intent clearly, and do related things live together while unrelated things stay separate?
The core problem here isn't just concurrency; it's that BatchImporter uses mutable instance fields to secretly pass data between private methods. This creates hidden coupling: each method silently depends on prior methods having run, which violates cohesion and makes the code fragile and hard to reason about.
D is correct because it attacks the root cause. Keeping invocation data local (method parameters and return values) makes data flow explicit and readable. If several values genuinely belong together — say, parsedRows and errors both describe the result of a parse step — grouping them in a context object is appropriate, but only when that grouping reflects real conceptual cohesion, not just "these fields happen to be needed."
A is wrong because synchronized merely serializes access to broken design. The hidden state and implicit method ordering remain, and you've only treated the concurrency symptom without improving clarity or cohesion.
B is similarly a symptom fix. ThreadLocal isolates threads from each other but leaves the tangled, order-dependent field-based design completely intact. Readability actually worsens because the data flow becomes even harder to trace.
C avoids cross-request corruption by constructing fresh instances, but the private methods still rely on fields being populated in sequence — the cohesion and readability problems are untouched.
As a strategy: when a question mentions both readability and cohesion, look for the answer that restructures data flow, not just access control.
- Which constructor organization best satisfies these requirements?
Answer: Have shorter constructors delegate with this(...) to the complete constructor, which performs validation and assigns every final field once.
When you see a question about constructor design in Java, think about two core constraints: final fields must be assigned exactly once, and duplicated logic is a maintenance risk. The goal is a single source of truth for both validation and field assignment.
The best pattern here is **constructor chaining via this(...)**, which is exactly what D describes. The one- and two-argument constructors supply defaults and immediately delegate to the full three-argument constructor using this(host, DEFAULT_PORT, DEFAULT_TLS), etc. That complete constructor is the only place validation runs and the only place fields are assigned. Every construction path funnels through it, so there's zero duplication and no way to accidentally skip validation.
A is tempting because calling a shared validation method sounds clean, but each constructor still assigns its own fields independently. This means you have repeated field-assignment logic scattered across constructors — a violation of DRY and a future bug waiting to happen if one constructor is updated inconsistently.
B describes a fundamental impossibility in Java. Once a constructor assigns a final field, no other constructor can reassign it. There's no "replace afterward" for final fields — the compiler simply won't allow it.
C using a static factory could work as an architectural pattern in general, but the requirement explicitly says the existing constructor signatures must remain available. A factory returning new instances doesn't satisfy that constraint, and it sidesteps the constructor organization question entirely.
Remember this rule: shorter constructors should call longer ones, never the reverse. When you chain with this(...), defaults flow toward the complete constructor, keeping validation in one place and honoring Java's final field rules.
- Which package organization best matches these change and visibility patterns?
Answer: Create top-level orders and billing packages, colocate each feature's layers, and place only genuinely shared stable types in a small shared package.
When designing package structures in Java, the key question to ask is: what changes together, and what needs to stay hidden from what? This question tests your understanding of package-by-feature versus package-by-layer organization and how Java's access modifiers (especially package-private) support encapsulation.
The passage tells you two things directly: changes typically span one feature's full stack, and the features should not expose their internals to each other. This maps perfectly to B — organizing by feature (orders, billing) keeps each feature's layers colocated, so a change touching controller, service, and model in one feature stays within one package boundary. The small shared package handles only the stable, genuinely cross-cutting types (identifiers, money), keeping that coupling minimal and intentional.
A is the classic package-by-layer approach, and it's wrong here because it groups classes by technical role rather than business feature. A single-feature change now scatters across four packages, making navigation harder and forcing you to make internals public just so layers in different packages can communicate.
C dumps everything into one package, which destroys all encapsulation. Package-private access becomes meaningless when every class can see every other class — you've traded structure for chaos as the codebase grows.
D sounds like it solves the coupling problem, but duplicating shared types creates a worse problem: two definitions of "money" or "order ID" that will diverge over time, causing subtle bugs and requiring redundant maintenance.
A useful rule of thumb: package boundaries should mirror team or feature ownership boundaries, not just technical layers. When a question describes change patterns, let those patterns drive your package design.
- Which reorganization best preserves cohesion while limiting the impact of future changes?
Answer: Let a report service coordinate a repository, summary calculator, renderer implementation, and destination implementation, each with a focused responsibility.
When a system needs to support multiple independent variations — like output formats and storage destinations — the key design principle to reach for is separation of concerns, often formalized through patterns like Strategy or Dependency Injection. Ask yourself: if two things change for different reasons, should they live together?
Option B is the right reorganization because it gives each responsibility its own focused component — the repository fetches data, the calculator computes totals, the renderer handles formatting, and the destination handles storage. These can be swapped independently. Want to add JSON? Provide a new renderer. Want cloud storage? Provide a new destination. The coordinating service stays stable, and no other component needs to change. This is the essence of high cohesion (each class does one thing well) and low coupling (components don't depend on each other's internals).
Option A creates a subclass explosion — two formats times two destinations already gives you four subclasses, and adding a third format means adding more subclasses for every existing destination. This is the exact problem the Bridge pattern exists to solve. Option C keeps everything in one class with conditional logic, which means every new format or destination forces you to modify the same file, increasing the risk of regressions and violating the Open/Closed Principle. Option D moves behavior into the transaction entity, which is a domain object — mixing report-rendering and storage concerns into data classes destroys cohesion and creates a bloated, fragile model.
A reliable study tip: when you see independent axes of variation (format and destination), that's your signal that composition beats inheritance, and that each axis deserves its own abstraction.
- Which refactoring best organizes the code around cohesive responsibilities?
Answer: Let fulfillOrder coordinate an order validator, inventory repository, and notifier, with each collaborator owning its focused implementation details.
When you see a question about organizing code responsibilities, think Single Responsibility Principle (SRP) and separation of concerns — each class or object should own one focused area of behavior, making it independently testable, swappable, and readable.
The best design has fulfillOrder act purely as a coordinator: it delegates to specialized collaborators — a validator, an inventory repository, and a notifier — each encapsulating their own implementation details. This is exactly what A describes. Need to test validation rules in isolation? Inject a mock validator. Want to swap JDBC for JPA? Replace the repository without touching anything else. Adding a second notification channel? Extend the notifier without rewriting fulfillment logic. The method remains a clean, readable sequence of delegation calls.
B is tempting because extracting private methods feels like organizing code, but it's a surface-level refactoring. JDBC and email dependencies stay inside the fulfillment class, so it still has multiple reasons to change — violating SRP and making the class hard to test or swap out independently.
C pushes behavior into the Order entity, which is a data-focused domain object. Loading it with validation logic, inventory updates, and email construction turns it into a bloated "God object," violating SRP in a different direction and coupling infrastructure concerns to your domain model.
D reaches for inheritance to handle variation in persistence and notification. This creates a combinatorial explosion of subclasses — two persistence options × two notification channels already means four classes — when composition (injecting collaborators) is almost always the more flexible solution.
A practical tip: when a method does too much, ask who should own each concern before reaching for private methods or inheritance. Composition through collaborators is usually the answer.
- Where and how should
parseLine be organized? Answer: Keep it in CsvImporter as a private static helper because its scope and state requirements are limited to that class.
When designing helper methods in Java, you should always ask three questions: Who needs this? Does it depend on instance state? And what is its logical scope? This question tests your understanding of encapsulation and the appropriate use of access modifiers and static context.
parseLine checks all the boxes for a private static helper: it uses no instance fields, it serves only CsvImporter, and its behavior is fully testable through importFile. Making it private static in CsvImporter signals both of these facts clearly — static because it needs no instance, private because nothing outside the class should depend on it. That makes C the right choice.
A is tempting if you're thinking about reusability, but reusability should follow demonstrated need, not speculation. The quoting rules are importer-specific — publishing them in a CsvUtils class leaks implementation details into a shared API and creates accidental coupling. Premature generalization is a common design mistake.
B makes it a non-static instance method, which is misleading because the method doesn't use instance state. Worse, making it package-private just to ease testing is a code smell — you're widening the method's visibility and coupling your design to your test strategy rather than letting tests work through the public API (importFile).
D violates the Single Responsibility Principle. CsvRow is a data model; giving it parsing logic that understands quoting rules entangles two separate concerns — representation and parsing.
A good rule of thumb: minimize both visibility and scope. If a helper needs no instance state and belongs to one class, make it private static. Don't expand visibility just for testing convenience.