The Law of Demeter is the most violated principle in object-oriented code, and the most
expensive to violate. Its formal statement — "only talk to your immediate friends" — sounds
like a social guideline. It is a structural one. Every violation creates a message chain —
a.b().c().d() — where your code depends not just on the type of a, but on the internal
structure of every object in the chain. When any object in the chain changes, your code breaks.
The law exists to prevent this: each message chain is a hidden coupling that compounds across
the codebase.
The rule, stated precisely
The Law of Demeter (1987) says: a method M of object O should only invoke methods on:
Oitself- Objects passed as parameters to
M - Objects created by
M - Objects in
O's instance variables
In other words: do not call methods on objects you obtained by calling methods on other objects.
# violation: message chain
order = get_order(id)
total = order.get_customer().get_payment_method().get_balance()
# compliance: tell, don't ask
total = order.available_balance()The violation tells you three things about the implementation: the order knows about customers, customers know about payment methods, and payment methods have balances. The compliant version tells you nothing about the implementation — the order exposes the information you need directly.
Why message chains are dangerous
A message chain a.b().c().d() creates a dependency chain — your code depends on the
types of a, the return type of b(), the return type of c(), and the existence of d().
That is four types your code is coupled to. If any type in the chain changes — c() returns
a different type, d() is renamed, b() returns null — your code breaks.
The damage compounds:
class Order:
def get_customer(self): return self._customer
class Customer:
def get_payment_method(self): return self._payment_method
class PaymentMethod:
def get_balance(self): return self._balance
# 50 places in the codebase do this:
balance = order.get_customer().get_payment_method().get_balance()If you rename get_balance() to current_balance(), you break 50 call sites. If you change
the return type of get_payment_method(), you break 50 call sites. If you remove the
Customer dependency from Order, you break 50 call sites.
The compliant version centralizes the dependency:
class Order:
def available_balance(self):
return self._payment_method.balance
# 50 call sites do this:
balance = order.available_balance()If the internal structure changes, you change one method in one class. The 50 call sites are unaffected. The coupling is absorbed by the class that owns the data, not spread across the codebase.
The refactoring: move the method to the owner
The fix for every Demeter violation is the same: move the method to the object that owns the data.
# before: telling a story about the implementation
customer = order.get_customer()
address = customer.get_address()
city = address.get_city()
# after: asking a question about the domain
city = order.shipping_city()The method shipping_city() belongs on Order because the order knows its shipping city.
Whether that information comes from a customer, an address, or a database is irrelevant to
the caller. The caller asks a question about the order; the order answers.
This is sometimes called the Facade pattern at the method level — each method is a facade over the internal object graph. The facade absorbs the complexity of the internal structure; the caller never sees it.
The three violation patterns
1. The train wreck: a.b().c().d()
The classic violation. Each dot creates a dependency on a new type.
// violation
String city = order.getCustomer().getAddress().getCity();
// compliance
String city = order.getShippingCity();2. The hidden dependency: passing objects to strangers
# violation: Order exposes Customer to PaymentService
def process_payment(order):
customer = order.get_customer()
payment_service.charge(customer)
# compliance: Order processes its own payment
def process_payment(order):
order.process_payment()The function process_payment should not know that customers have payment methods. That is
the order's responsibility.
3. The callback chain: nested callbacks that reach into objects
// violation
server.handleRequest(function(req) {
var user = req.getUser();
var prefs = user.getPreferences();
var theme = prefs.getTheme();
render(theme);
});
// compliance
server.handleRequest(function(req) {
render(req.userTheme());
});When to deliberately violate the law
The law is not absolute. There are cases where compliance creates more complexity than violation:
1. Builder/fluent interfaces. builder.withName("x").withAge(30).build() — the chain is
the API. The builder owns all the intermediate objects, so the coupling is contained.
2. Collection traversal. list.get(0).getName() — when the collection is the immediate
friend and the elements are known to be of a specific type.
3. Framework code. Frameworks like Spring or Django create object graphs that callers traverse. The framework owns the graph; the caller is expected to traverse it.
4. When the facade method would be trivial. If order.getCustomer().getName() is called
in one place and the "facade" method would be return _customer.getName(), the facade adds
a layer of indirection with no benefit.
The rule of thumb: violate the law when the coupling is already bounded (builder, framework, single call site). Follow the law when the coupling would propagate across multiple call sites or classes.
The testing benefit
Demeter-compliant code is easier to test because objects talk to their friends, not to strangers:
# testing the violation: must mock the entire chain
order = Mock()
order.get_customer.return_value.get_payment_method.return_value.get_balance.return_value = 100
# testing the compliance: mock one object
order = Mock()
order.available_balance.return_value = 100The first test knows the internal structure of the payment system. The second test knows only what the order exposes. When the payment system changes, the first test breaks. The second test is unaffected.
This is the structural test benefit: compliant code has a smaller test surface. The mocks are simpler, the test setup is shorter, and the test is resilient to internal changes.
What this means for your code
-
Count the dots.
a.b().c()is two dots — two dependencies.a.b().c().d()is three. If the chain has more than one dot, consider moving the method. -
Move methods to the owner. The object that owns the data should expose it. The caller asks a question; the object answers. The caller should not have to navigate the object graph to find the answer.
-
Prefer "tell, don't ask." Instead of asking an object for data and then operating on it, tell the object to do the operation.
order.process_payment()instead ofpayment_service.charge(order.get_customer()). -
Audit periodically. Run
grep -rn '\.\w\+(\.\w\+()' .on your codebase to find message chains. Each one is a coupling point. -
Teach new team members the rule early. Demeter violations are easy to write and hard to remove. The best time to prevent them is at code review, not at refactoring.