Skip to main content
Clean Code 60 XP · 6 min

Objects vs Data Structures

Objects hide data behind behavior. Structures expose data. Mixing both creates hybrids nobody wants.

Showing
Ad (728×90)

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 layer

Code 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.

Done with this lesson?

Mark it complete to earn XP and track your progress.