Objects vs Data Structures
Objects hide data behind behavior. Structures expose data. Mixing both creates hybrids nobody wants.
The Fundamental Difference
Objects hide their data and expose behavior (methods). Data structures expose their data and have no meaningful behavior. This isn't aesthetic — it determines how your code handles change. Adding a new operation to a data-structure world is easy (add a function). Adding a new type to an object world is easy (add a subclass). Each is hard for the other case.
// Data Structure — expose data, no behavior
struct Point:
x
y
// Operates on data structures (procedural)
function distance(p):
return sqrt(p.x * p.x + p.y * p.y)
// ─────────────────────────────────────────────
// Object — hides data, exposes behavior
class Point:
_x = 0
_y = 0
function distanceToOrigin():
return sqrt(_x * _x + _y * _y)
function translate(dx, dy):
return new Point(_x + dx, _y + dy)The Law of Demeter
A method should only call methods on: itself, its arguments, objects it created, or its direct components. Never reach into an object to get another object and call that. Train wrecks — a.getB().getC().doSomething() — expose implementation details across the entire chain and make refactoring a nightmare.
Train wreck — violates Law of Demeter
amount = order.getCustomer()
.getWallet().getBalance()
if order.getCustomer()
.getWallet().getBalance()
> order.total:
order.getCustomer()
.getWallet().deduct(order.total)
Tell, don't ask — delegate to the right owner
order.chargeCustomer()
// Inside Order:
function chargeCustomer():
customer.charge(total)
// Inside Customer:
function charge(amount):
wallet.deduct(amount)
DTOs and Active Records
Data Transfer Objects (DTOs) are pure data structures — no business logic. Use them to move data between layers: HTTP response → service, database row → domain. Active Records add save()/find() to DTOs. The trap: never add business logic to an Active Record — create a separate domain object for that.
// ✓ DTO — pure data, no behavior
struct UserDTO:
id
name
email
createdAt
// ✓ Active Record — data + persistence only
class UserRecord:
id
name
email
function save(): ...
function find(id): ...
// ✗ Hybrid — Active Record with business logic (don't do this)
class UserRecord:
...
function canAccessAdmin(): ... // ← business logic here
function calculateSubscriptionPrice(): ... // ← wrong layerCode Challenge
Break the Train Wreck — the chained calls expose three internal objects. Delegate to the right owner at each level.
💡Key takeaway
Objects are not better than data structures — they're different tools. Pick one and commit. Hybrids give you the worst of both.
🔧 Some exercises may still have errors. If something seems wrong, use the Feedback button (bottom-right of the page) to report it — it helps us fix it fast.
Hint: Ask: who *owns* each piece of data in the chain? The order shouldn't know about city. Ask order for getShippingCity() and let order delegate internally — the caller stays ignorant of the internal structure.
✗ Your version