Updated on 22 Jun, 202616 mins read 25 views

Imagine you are working on an e-commerce system.

You need to get a customer's city.

A developer writes:

order
    .getCustomer()
    .getAddress()
    .getCity()
    .getName();

Looks harmless.

A few months later:

The Address model changes.

Customer
    └── Location
            └── City

instead of:

Customer
    └── Address
            └── City

Now hundreds of places break.

Why?

Because many parts of the system knew:

Customer Internals

and

Address Internals

The code became tightly coupled to the internal object structure.

This exact problem led to: Law of Demeter

Historical Background

The Law of Demeter was developed at: Northeastern University during the Demeter Project.

The primary researcher was: Karl Lieberherr.

The idea emerged from observing large software systems.

Teams repeatedly encountered:

High Coupling
Fragile Code
Difficult Refactoring

A common root cause:

Objects knew too much about other objects.

The solution:

Limit what an object is allowed to know.

Hence: Principle of Least Knowledge

Official Definition

An object should only talk to its immediate friends.

Or more formally:

A method should only invoke methods belonging to:

1 Itself

this->calculateTax();

2 Its Direct Members

repository.save();

3 Objects Created Inside The Method

Logger logger;
logger.log();

4 Parameters Passed To The Method

void process(Customer& customer)
{
    customer.activate();
}

Not arbitrary objects deep inside other objects.

The Intuition

Suppose you work in a company.

You need a document from Finance.

Bad approach:

Employee
    -> Manager
        -> Director
            -> VP
                -> CEO
                    -> Finance Team

You navigate the entire organizational hierarchy.

Manager Structure

Director Structure

VP Structure

CEO Structure

Any organizational change breaks the process.

Better:

Employee
    -> Manager

The manger gets the document.

The employee only knows:

Its Direct Contact

The employee doesn't need to know:

  • Directors
  • VPs
  • Finance structure

This is exactly the Law of Demeter.

The Core Idea

The principle can be summarized as:

An object should know as little as possible about the internal structure of other objects.

Notice:

It does NOT say:

Don't use object.

It does NOT say:

Don't call methods.

It says:

Don't depend on internals

The Famous Rule

A method should only talk to:

Self
Direct Friends

Not:

Friends of Friends

Think:

Don't talk to strangers

Understanding Object Graphs

Consider:

class City
{
};

class Address
{
public:
    City city;
};

class Customer
{
public:
    Address address;
};

class Order
{
public:
    Customer customer;
};

Usage:

order.customer.address.city.getName();

Object graph:

Order
  |
Customer
  |
Address
  |
City

The caller traverse:

4 Levels Deep

This creates strong coupling.

Train Wreck Code

One of the easiest Demeter violations to recognize.

Example:

order
    .getCustomer()
    .getAddress()
    .getCity()
    .getName();

Looks like:

--------->

a train.

Hence the nickname:

Train Wreck

Another example:

user
    .getProfile()
    .getSettings()
    .getTheme()
    .getColor();

The caller depends on:

Profile
Settings
Theme

Huge coupling.

Why Train Wrecks Are Dangerous

Suppose:

Address

becomes:

Location

Now:

order
	.getCustomer()
	.getAddress()

breaks everywhere.

A single internal change causes:

Massive Ripple Effects

This is fragile design.

Better Design:

Instead of:

order
	.getCustomer()
	.getAddress()
	.getCityName();

Provide behavior:

class Customer
{
public:

    std::string getCityName() const;
};

Usage:

order
	.getCustomer()
	.getCityName();

Now:

Address Structure Hidden

Encapsulation preserved.

Tell, Don't Ask

The Law of Demeter naturally leads to another major design principle:

Tell, Don't Ask

Bad:

if (order.getCustomer()
	.getMembership()
	.getLevel() == GOLD)
{
	discount = 20;
}

The caller extracts data.

Then make decisions.

Better:

if (order.isEligibleForGoldDiscount())
{
	discount = 20;
}

The object owns the decision.

The caller simply asks for behavior.

This creates better object-oriented design.

Fluent APIs: An Important Exception

Many students become confused here.

Consider:

query
    .select("*")
    .where("id=10")
    .orderBy("name")
    .execute();

Looks like a train wreck.

But it usually isn't.

Why?

Because:

select()
where()
orderBy()

return:

*this

the same object.

You are not traversing:

Object A
 -> Object B
 -> Object C
 -> Object D

You are repeatedly operating on:

Object A

Therefore fluent APIs are usually acceptable.

Buy Me A Coffee

Leave a comment

Your email address will not be published. Required fields are marked *