Chapter 10 Flashcards — Classes

flashcards clean-code classes srp solid cohesion

What is the correct Java convention for organizing a class’s members top-to-bottom?
?

  1. public static final constants. 2. private static variables. 3. private instance variables. 4. Public constructors. 5. Public methods. 6. Private utility methods placed near the public method that calls them. This order creates a logical reading flow and tells readers exactly where to look for each kind of member.

How do you measure the “size” of a class in Clean Code?
?
By responsibilities — not lines of code, not number of methods. A 50-line class with three unrelated responsibilities is too large. A 200-line class focused on a single responsibility may be perfectly sized. The test: can you describe the class in 25 words or fewer without using “and”, “or”, “but”, or “if”? If not, it’s too large.

What does the Single Responsibility Principle (SRP) state?
?
A class or module should have one, and only one, reason to change. “Reason to change” = responsibility. A class that handles authentication AND sends emails AND manages user profiles has three reasons to change — a change in email templates forces a modification to the same class that handles auth logic, risking unintended breakage.

Why is SRP the most important of the SOLID principles?
?
SRP ensures predictable, safe modifications. When a class has one reason to change, you know exactly what could cause it to change and what it could break. When a class has multiple reasons to change, modifications for one concern can accidentally break another. SRP makes the blast radius of every change small and well-defined.

What are the warning signs that a class violates SRP?
?

  1. Name contains “Manager”, “Processor”, “Handler”, “Super” — these names admit the class does too much. 2. The description requires conjunctions: “handles payments and sends emails”. 3. Methods fall into two clearly distinct groups that use different subsets of instance variables. 4. Testing one behavior requires setting up an unrelated subsystem.

What is cohesion in the context of a class?
?
Cohesion measures how closely the methods and instance variables of a class are related to each other. A highly cohesive class has methods that each use most of the instance variables — they all work with the same data. A low-cohesion class has methods that use disjoint subsets of variables, indicating the class contains multiple unrelated concerns.

How does refactoring large functions into smaller ones sometimes break cohesion?
?
When you split a large function, the smaller functions often need to share data. If you make that shared data instance variables (to avoid long argument lists), the class now has variables that only some methods use — cohesion drops. The fix is to recognize that these “clusters” of methods and variables form a new class that should be extracted.

What does the Open/Closed Principle (OCP) state?
?
A class should be open for extension but closed for modification. New behaviors should be added by adding new code (a subclass, a decorator, a new strategy implementation) rather than by changing existing, working code. Modifying existing code risks breaking tested behavior; adding new code does not.

What is the canonical example of OCP from Chapter 10?
?
The Sql class generating SQL strings. The bad version is one class with select(), insert(), update() methods — adding update() means modifying the class. The good version is an abstract Sql base class with SelectSql, InsertSql, UpdateSql subclasses — adding UPDATE means adding UpdateSql, with zero changes to SelectSql or InsertSql.

What is the Dependency Inversion Principle (DIP)?
?
High-level modules should not depend on low-level modules. Both should depend on abstractions. In practice: a business-logic class like Portfolio should depend on a StockExchange interface, not on TokyoStockExchange directly. This decouples the business logic from infrastructure details and allows tests to inject a FixedStockExchange fake.

Why does violating DIP make code untestable?
?
When a class directly constructs its dependencies (e.g., new TokyoStockExchange() inside Portfolio), there is no seam for a test to inject a fake. The test is forced to use the real implementation, which may make real network calls, require a database, or be non-deterministic. DIP creates the injection point (constructor parameter, method parameter) that tests use to substitute a fast, deterministic fake.

What is constructor injection and why is it the preferred form of DIP?
?
Constructor injection means accepting dependencies through the constructor rather than constructing them inside methods. It is preferred because: 1. Dependencies are explicit in the constructor signature — callers know what is required. 2. The object is fully configured at construction time — no partially initialized state. 3. It works without a DI framework — tests can pass fakes directly. Compare: new Portfolio(stockExchange) vs new Portfolio() with new TokyoStockExchange() inside.

What is the Liskov Substitution Principle (LSP)?
?
Subtypes must be substitutable for their base types without altering the correctness of the program. If code is written to work with a StockExchange, any concrete StockExchange implementation should work in its place without surprising behavior. Common violation: a subclass override that throws an exception the base class contract does not declare, or weakens a precondition the caller relies on.

What is the Interface Segregation Principle (ISP)?
?
Clients should not be forced to depend on methods they do not use. A fat interface that forces implementors to stub out unused methods is a violation. The fix is to split the interface into smaller, role-specific interfaces. Example: a User interface with read(), write(), delete() should be split into Readable, Writable, Deletable — a read-only client depends only on Readable.

What does “many small classes” mean in practice, and why is it better than a few large ones?
?
“Many small classes” means each class has a narrow, precise responsibility with high cohesion. It is better because: 1. Each class is independently testable. 2. Changes to one class don’t risk breaking another. 3. New behaviors are added by creating new classes, not modifying existing ones. 4. The system’s structure documents itself — class names tell you what each piece does. The fear of “too many files” is unfounded; it’s easier to find things in well-named small files than in large omnibus files.

How does the “reason to change” framing help identify SRP violations?
?
For every potential change in requirements, ask: “which class would need to change?” If the answer is the same class for two unrelated requirement changes, that class has multiple responsibilities. Example: if both “change the email template” and “change the password hashing algorithm” require modifying UserService, then UserService has at least two responsibilities (notifications + authentication).

What is the relationship between DIP and testability (the Timely FIRST principle)?
?
DIP is a precondition for Timely tests. When production code is designed with DIP from the start (test-first TDD), it naturally has interfaces and constructor injection — because writing the test forces you to think “how will I inject a fake here?” Code written without DIP first is often discovered to be untestable only after the fact, requiring expensive rewrites. DIP and Timely reinforce each other.

What is a “God Class” and why is it harmful?
?
A God Class is a class that knows too much and does too much — it accumulates functionality over time because it already has most of the relevant state. It typically has dozens of methods across several unrelated domains, many instance variables, and dependencies on numerous subsystems. It is harmful because: 1. It is the central point of coupling — everything depends on it. 2. It cannot be unit tested without wiring up the entire system. 3. It becomes a merge conflict hotspot when multiple developers work on it.

In Python, what is Protocol and how does it support DIP?
?
Protocol (from typing) is Python’s mechanism for structural subtyping — also called “duck typing with static checking”. A class satisfies a Protocol if it has the required methods, without needing to explicitly inherit from it. This is ideal for DIP: Portfolio depends on StockExchange(Protocol), and FixedStockExchange (the test fake) satisfies the protocol just by implementing get_price() — no explicit class FixedStockExchange(StockExchange) needed.

In C++, how do you represent an interface and enforce DIP?
?
In C++, an interface is an abstract class with only pure virtual methods (= 0) and a virtual destructor. Example: class StockExchange { public: virtual ~StockExchange() = default; virtual double getPrice(const std::string&) const = 0; };. DIP is enforced by having Portfolio accept a std::unique_ptr<StockExchange> in its constructor — ownership is explicit, implementation is swappable, and the destructor is always virtual to avoid UB.

When is it appropriate to loosen encapsulation (make something protected or public) in a class?
?
Encapsulation should be loosened only to support tests, and only to protected (not public) when possible. Example: a private utility method may be made protected so a test subclass can call it directly. The principle: always try private first; promote only when a test genuinely requires access. Never loosen encapsulation to satisfy a caller in production code — that breaks the abstraction.

What’s the difference between SRP and cohesion? Are they the same thing?
?
They are related but not identical. SRP is about reasons to change — a class has one if it has a single, well-defined responsibility. Cohesion is about how closely the methods and variables of a class relate to each other — a cohesive class has all methods working with most of its variables. A high-cohesion class tends to satisfy SRP (all the code belongs together), but cohesion is a structural metric while SRP is a semantic/change-oriented concern.

What does OCP look like when applied to a payment processing system?
?
A PaymentProcessor that handles credit cards, PayPal, and crypto with nested if-else or switch violates OCP — adding a new payment method requires modifying PaymentProcessor. The OCP-compliant version defines a PaymentMethod interface with a process(amount) method. CreditCardPayment, PayPalPayment, CryptoPayment implement it. Adding a new payment type means adding a new class, zero changes to PaymentProcessor.

Why is new ConcreteClass() inside a constructor (not via injection) a red flag?
?
It hardcodes the dependency — the class can never be tested or deployed with a different implementation. It means the class constructs its own collaborators, which is a construction/use separation violation (see ch11-systems). The constructor should use dependencies to configure the object, not construct the dependencies themselves. Use constructor injection: accept the dependency as a parameter.

What makes a class name a good indicator of SRP compliance?
?
A good class name is precise and domain-specific: AuthenticationService, OrderPriceCalculator, InvoicePdfGenerator. A bad class name is vague or compound: UserManager, DataProcessor, ServiceHelper, UserAndOrderHandler. The rule: if the name requires a vague word (Manager, Processor, Helper) or a conjunction (AndOr), the class is doing too many things and needs to be split until it earns a precise name.

What is the key difference between extension (OCP) and modification, and when should you choose each?
?
Extension adds new behavior without changing existing code — add a subclass, add a decorator, add a new strategy. Use extension when existing code is stable, tested, and the new behavior is a new variant of an existing abstraction. Modification changes existing code — use it when there is a bug in existing behavior that must be fixed, or when the abstraction itself is wrong. OCP is not “never modify” — it’s “don’t modify to add new variants; extend instead.”

How does the “extract class” refactoring relate to cohesion?
?
Extract class is the primary refactoring for restoring cohesion. When you notice that a subset of methods uses a distinct subset of instance variables (and rarely interacts with the other methods/variables), those methods and variables form a natural cluster — they belong in their own class. Extracting them increases cohesion in both the original class and the new class, and often reveals a hidden SRP violation.

Total Cards: 27
Review Time: ~18 minutes
Priority: HIGH
Last Updated: 2026-04-14