law-of-demeter
Use when accessing nested object properties. Use when chaining method calls. Use when reaching through objects to get data.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Use when accessing nested object properties. Use when chaining method calls. Use when reaching through objects to get data.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Use when writing tests. Use when test structure is unclear. Use when arrange/act/assert phases are mixed.
Use when designing or modifying APIs. Use when adding breaking changes. Use when clients depend on API stability.
Use when implementing authentication. Use when storing passwords. Use when asked to store credentials insecurely.
Use when same data is fetched repeatedly. Use when database queries are slow. Use when implementing caching without invalidation strategy.
Use when tempted to use class inheritance. Use when creating class hierarchies. Use when subclass needs only some parent behavior.
Use when acquiring multiple locks. Use when operations wait for each other. Use when system hangs without crashing.
| name | law-of-demeter |
| description | Use when accessing nested object properties. Use when chaining method calls. Use when reaching through objects to get data. |
Only talk to your immediate friends, not strangers.
A method should only call methods on: itself, its parameters, objects it creates, or its direct components. Never reach through an object to access another object's internals.
obj.a.b.cobj.getA().getB().getC()NEVER chain through objects. Ask, don't reach.
No exceptions:
If you see multiple dots, you're violating LoD:
// ❌ VIOLATION: Reaching through objects
function getEmployeeCity(company: Company, employeeId: string): string {
return company.employees
.find(e => e.id === employeeId)
?.address.city; // Reaching into employee, then into address
}
// More violations:
user.getProfile().getAddress().getZipCode();
order.getCustomer().getPaymentMethod().getLast4();
Let objects expose what's needed:
// ✅ CORRECT: Ask the object directly
class Employee {
constructor(
private name: string,
private address: Address
) {}
getCity(): string {
return this.address.city; // Employee asks its own address
}
}
class Company {
getEmployeeCities(): Map<string, string> {
return new Map(
this.employees.map(e => [e.id, e.getCity()])
);
}
getEmployeeCity(employeeId: string): string | undefined {
return this.employees.find(e => e.id === employeeId)?.getCity();
}
}
// Usage: Ask company, don't reach through it
const city = company.getEmployeeCity(employeeId);
| Problem | Impact |
|---|---|
| Tight coupling | Caller knows internal structure |
| Fragile code | Structure changes break all callers |
| Hidden dependencies | Not obvious what's needed |
| Hard to test | Must mock entire chain |
| Null danger | Each . is a potential null |
A method m of class C should only call methods on:
this - C's own methodsmm createsclass OrderProcessor {
constructor(private logger: Logger) {} // Component
process(order: Order): Receipt { // Parameter
this.validate(order); // this
const receipt = new Receipt(order); // Created
this.logger.log('Processed'); // Component
return receipt;
}
// ❌ NOT ALLOWED: order.customer.address.city
// ✅ ALLOWED: order.getShippingCity()
}
Pressure: "One line with dots is simpler than adding methods"
Response: Simple to write ≠ simple to maintain. Chains create fragile code.
Action: Add methods that expose needed data.
Pressure: "It's only two dots, not a big deal"
Response: Two dots = two objects you're coupled to. Both can change and break you.
Action: Even short chains should be eliminated.
Pressure: "The structure has the data, why wrap it?"
Response: Structure changes. Wrapping isolates you from changes.
Action: Ask the owner for the data.
Pressure: "I'm just reading, not modifying"
Response: Reading through chains still couples you to structure.
Action: Ask for what you need.
If you notice ANY of these, refactor:
a.b.c.dgetA().getB().getC()a?.b?.c?.dAll of these mean: Add a method to ask directly.
// ❌ BEFORE: Chain
const zip = user.getProfile().getAddress().getZipCode();
// ✅ AFTER: Ask
// In User class:
getZipCode(): string {
return this.profile.getZipCode();
}
// In Profile class:
getZipCode(): string {
return this.address.zipCode;
}
// Usage:
const zip = user.getZipCode();
| Chain (Bad) | Ask (Good) |
|---|---|
company.employees[0].address.city | company.getEmployeeCity(id) |
order.customer.paymentMethod.last4 | order.getPaymentLast4() |
user.profile.settings.theme | user.getTheme() |
car.engine.fuel.level | car.getFuelLevel() |
| Excuse | Reality |
|---|---|
| "It's simpler" | Chains are simpler to write, harder to maintain. |
| "Just one chain" | One chain = multiple couplings. |
| "Data is right there" | Expose it properly through methods. |
| "It's read-only" | Reading chains still couples you. |
| "Fewer lines" | Lines don't matter. Maintainability does. |
| "It's obvious what it does" | Obvious coupling is still coupling. |
Ask objects for what you need. Don't reach through them.
When you need data from nested objects: add a method on the owner that returns it. Never chain through multiple objects. Each dot is a dependency you're taking on.