Clean&Reactoring Clean Code Created: 09 Feb 2026 Updated: 09 Feb 2026

the Law of Demeter

In software engineering, we often talk about "coupling"—the degree to which one class relies on the internal details of another. One of the most famous guidelines for reducing coupling is the Law of Demeter (LoD), often summarized by the rule: "Don't talk to strangers."

The Law of Demeter challenges developers to write "shy" code. An object should know as little as possible about the structure or properties of anything else, including its own sub-components.

The Problem: "Train Wrecks" and Dot Counting

A violation of the Law of Demeter is often visually obvious. It is characterized by a long chain of accessors, often called a "navigation train" or a "train wreck."

When you see code like A.B.C.D.Method(), object A is showing that it knows exactly how B is built, how C is built, and where D lives. If the architecture of B changes, A breaks.

The Rules of Demeter

To obey the law, a method M in object O should only invoke methods of:

  1. Object O itself.
  2. The parameters passed to M.
  3. Any objects created within M.
  4. Direct component objects (fields/properties) of O.

Let's look at a concrete C# scenario involving a Reporting system.

❌ The Bad Example: The Violation

In this scenario, a Client class wants to run a report. However, to do so, it reaches deep into the Report object hierarchy to manually open a database connection.

namespace LoD.Bad
{
public class Connection
{
public void Open()
{
Console.WriteLine("Connection Opened.");
}
}

public class Database
{
public Connection Connection { get; private set; } = new Connection();
}

public class Report
{
public Database Database { get; private set; } = new Database();
}

public class ReportRunner
{
public void Run()
{
var report = new Report();

// ❌ VIOLATION: The "Train Wreck"
// The Runner knows that Report has a Database.
// It knows the Database has a Connection.
// It knows the Connection has an Open method.
report.Database.Connection.Open();
Console.WriteLine("Report logic running...");
}
}
}

Why is this bad?

  1. Tight Coupling: ReportRunner is coupled to Database and Connection. If you change Report to use a cloud API instead of a Database, ReportRunner breaks.
  2. Testing Nightmares: To unit test ReportRunner, you have to mock Report, Database, and Connection.
  3. Lack of Encapsulation: The Report class is not in control of its own state. It is being micro-managed by the ReportRunner.

✅ The Good Example: "Tell, Don't Ask"

To fix this, we apply the "Tell, Don't Ask" principle. We shouldn't ask the Report for its database and then ask the database for its connection. We should simply tell the Report to prepare itself.

We hide the delegation logic inside the classes where it belongs.

namespace LoD.Good
{
public class Connection
{
public void Open()
{
Console.WriteLine("Connection Opened.");
}
}

public class Database
{
private readonly Connection _connection = new Connection();

// The Database creates a boundary.
// Consumers just say "Connect", they don't ask for the connection object.
public void Connect()
{
_connection.Open();
}
}

public class Report
{
private readonly Database _database = new Database();

// The Report exposes a high-level behavior.
// It hides the fact that a database is even involved.
public void Initialize()
{
_database.Connect();
}
}

public class ReportRunner
{
public void Run()
{
var report = new Report();

// ✅ SUCCESS: Law of Demeter obeyed.
// We only talk to our immediate friend (report).
// We don't care how it initializes internally.
report.Initialize();

Console.WriteLine("Report logic running...");
}
}
}

Why is this better?

  1. Loose Coupling: ReportRunner only depends on Report. You can completely swap out the internal Database for a CsvLoader without changing a single line of code in ReportRunner.
  2. Better Abstraction: The code reads like a sentence: report.Initialize(). This reveals intent rather than implementation.
  3. Easier Maintenance: Changes are localized. If the Connection logic changes, you only update the Database class.

Another Classic Analogy: The Paperboy

A famous analogy for the Law of Demeter is the "Paperboy and the Wallet."

The Violation (The Mugging)

Imagine a paperboy comes to collect payment from a customer.

  1. Code: customer.Wallet.GetMoney(5.00);
  2. Reality: The paperboy reaches into the customer's pocket, takes out their wallet, and grabs 5 dollars. This is rude and assumes the customer has a wallet.

The Adherence (The Transaction)

The paperboy should just ask for payment.

  1. Code: customer.Pay(5.00);
  2. Reality: The customer decides how to pay. They might use a wallet, a checkbook, or change in their pocket. The paperboy doesn't know (and doesn't care) where the money comes from.

Summary

FeatureViolation (Bad)Adherence (Good)
Code Stylea.GetB().GetC().DoSomething()a.DoSomething()
CouplingHigh (Tight)Low (Loose)
KnowledgeObject knows deep internal structureObject only knows interface of neighbors
MaintenanceFragile; changes ripple upwardsRobust; changes are contained


Share this lesson: