Chapter 6 Flashcards — Objects and Data Structures
flashcards clean-code objects data-structures law-of-demeter
What is the fundamental difference between an object and a data structure in Clean Code?
?
Objects hide their data behind abstractions and expose functions (behavior) that operate on that data. Data structures expose their data and have no meaningful functions. They are opposite design choices, not different names for the same thing.
Why is a class with getters and setters for every private field NOT necessarily an object?
?
Because getters and setters that directly mirror the private fields expose the internal representation — that is the definition of a data structure. True objects expose meaningful behavior, not field accessors. Adding getX()/setX() is ceremony, not abstraction.
What is the “Vehicle fuel abstraction” example from Clean Code and what does it illustrate?
?
getPercentFuelRemaining() (object interface) vs getFuelTankCapacityInGallons() + getGallonsOfGasoline() (data structure interface). The second forces the client to compute percentage and reveals the internal unit (gallons). The first hides representation — the implementation could use liters, percentage, or gallons internally.
What is the data/object anti-symmetry?
?
Objects make it easy to add new types (new subclass) but hard to add new functions (must change all classes). Data structures make it easy to add new functions (new procedure) but hard to add new types (must update all procedures). They are symmetric opposites.
When should you choose OO (objects/polymorphism) over procedural (data structures/functions)?
?
Choose OO when your system is more likely to grow by adding new types (new shapes, new payment methods, new shipping carriers). Choose procedural when your system is more likely to grow by adding new operations on existing data. The wrong choice makes the common change hard.
In the shapes example, why is Geometry.area(Shape s) with instanceof-checks considered procedural, not OO?
?
Because Geometry operates on exposed data structures (Square, Circle, Rectangle with public fields). Adding a new function (perimeter, diagonal) is easy — one place to change. Adding a new shape requires touching every function in Geometry. This is the procedural trade-off.
What is the Law of Demeter (state the formal rule)?
?
A method f of class C may only call methods of: (1) C itself, (2) an object created by f, (3) an object passed as an argument to f, (4) an object held in an instance variable of C. It may NOT call methods on objects returned by calling methods on those objects.
What is the colloquial summary of the Law of Demeter?
?
“Talk to friends, not to strangers.” A friend is any object you directly own or receive. A stranger is any object you obtain by navigating through another object’s internals.
What is a “train wreck” in code, and give an example?
?
A chain of method calls where each returns the next object: ctxt.getOptions().getScratchDir().getAbsolutePath(). Each .get...() navigates one level deeper into the object graph, coupling the caller to every intermediate type in the chain.
What are the two ways to fix a train wreck, and which is preferred?
?
- Intermediate variables — assign each step to a named variable. Better for readability, but still exposes internal structure. 2. Push behavior into the object —
ctxt.createScratchFileStream(classifier)instead of navigating to the path. This is preferred because it hides internal structure entirely.
Does the Law of Demeter apply to data structures? Give an example of acceptable chain navigation.
?
No. The Law of Demeter applies to objects (things with behavior). Navigating a DTO — address.city.postalCode — is perfectly acceptable because data structures are designed to have their data accessed. The violation is navigating through objects’ internal object graphs.
What is a hybrid class and why is it the “worst of both worlds”?
?
A hybrid is a class that has both meaningful behavior methods AND public variables or fully transparent getters/setters. It is hard to add new functions (like an object) AND hard to add new data types (like a data structure). It inherits the worst properties of each form.
What is a Data Transfer Object (DTO)?
?
A class with public variables and no meaningful functions. It is the pure data structure form. DTOs are appropriate at system boundaries: reading from a database, parsing network messages, transferring data between architectural layers. They carry data; they have no behavior.
What is the preferred Java syntax for a simple DTO in Java 16+ and why?
?
A record: public record OrderDto(String orderId, String customerId, double total) {}. Records auto-generate constructor, accessors, equals, hashCode, and toString. They are immutable by default and communicate “this is just data” clearly.
What is the C++ equivalent of a DTO, and what keyword signals it?
?
A struct with public data members. In C++, struct defaults to public access, which signals “this is a data container.” A class defaults to private access, signaling “behavior and hidden state.” Using the right keyword communicates intent.
What is the Python equivalent of a DTO and what decorator creates it?
?
A @dataclass — @dataclass class OrderDto: order_id: str; customer_id: str; total: float. For immutable DTOs, use @dataclass(frozen=True). The @dataclass decorator auto-generates __init__, __repr__, and __eq__.
What is an Active Record, and what is the mistake developers commonly make with it?
?
An Active Record is a DTO with navigational methods (save(), find(), delete()). The common mistake is adding business logic (discount calculations, eligibility checks, tier upgrades) to the Active Record. It should remain a data structure; business rules belong in a separate domain service.
Why should business logic NOT live in an Active Record?
?
Because the Active Record is a data structure — it models the persistence shape of an entity. Business logic in the Active Record creates a hybrid: you cannot add new operations without modifying the record class, and the class has too many reasons to change (persistence schema changes AND business rule changes).
What does “hiding structure” mean when applied to a ctxt object that has a scratch directory?
?
Instead of ctxt.getOptions().getScratchDir().getAbsolutePath() (navigating internal structure), ask ctxt to perform the work: ctxt.createScratchFileStream(classifier). The caller expresses intent (“I need a stream to write scratch data”) rather than navigation (“give me your options, then your scratch dir, then its path”).
How do you identify the right method name when fixing a train wreck?
?
Ask: “What is the caller ultimately trying to do?” If the caller navigates to getAbsolutePath() to open a file, the method should be named createOutputFile(name) or openScratchStream(classifier). The name reflects intent, not mechanism.
What is the difference between Point with getX()/getY() and Point with getR()/getTheta() in terms of abstraction?
?
getX()/getY() exposes the Cartesian representation — the interface locks in the implementation. getR()/getTheta() can represent a point stored in either Cartesian or polar form internally; the implementation is free to choose. The second interface abstracts over representation, which is what objects should do.
In the shapes example, under what circumstance is the procedural approach (Geometry.area(Shape s)) preferable to the OO approach?
?
When the set of shapes is stable (you rarely add new types) but the set of operations grows frequently. If you need to add perimeter(), diagonal(), boundingBox(), translate(), etc. over time, adding each is one function in Geometry — far easier than updating every Shape class.
What is the key question to ask before deciding between OO and procedural design for a new module?
?
“Which is more likely: adding new types, or adding new operations?” New types → OO (polymorphism). New operations → procedural (data structures + functions). If uncertain, procedural first; refactor to OO when you add the third or fourth type.
Is method chaining on a fluent builder a Law of Demeter violation? Why or why not?
?
No. In a fluent builder, every method in the chain returns the same builder object. The caller is always talking to the same friend (StringBuilder, QueryBuilder). The Law of Demeter violation occurs when a chain navigates to different, unrelated objects.
What does adding a getter that returns a primitive vs. a getter that returns an object tell you about Law of Demeter risk?
?
A getter returning a primitive (getBalance() → double) does not create a Demeter risk — you cannot call methods on a primitive. A getter returning an object (getOptions() → Options) creates Demeter risk because the caller may then call methods on the returned object, navigating deeper into the object graph.
What distinguishes a legitimate abstraction from a leaky one in an interface design?
?
A legitimate abstraction tells the caller what the object knows or can do, without revealing how it stores or computes it (getPercentFuelRemaining()). A leaky abstraction reveals the internal representation through the interface (getFuelTankCapacityInGallons() + getGallonsOfGasoline()). The test: can the implementation change without the caller noticing?
What pattern does Clean Code recommend for translating DTOs into domain objects?
?
A mapper or factory class that takes a DTO and returns a domain object: OrderMapper.toDomain(orderDto). The DTO carries raw data from the boundary (DB row, JSON payload); the mapper applies validation and construction logic; the domain object carries behavior. The three have different reasons to change.
Total Cards: 27
Review Time: ~20 minutes
Priority: HIGH
Last Updated: 2026-04-14