law-of-demeter

law-of-demeter

Use when accessing nested object properties. Use when chaining method calls. Use when reaching through objects to get data.

4звезд
0форков
Обновлено 1/23/2026
SKILL.md
readonlyread-only
name
law-of-demeter
description

Use when accessing nested object properties. Use when chaining method calls. Use when reaching through objects to get data.

Law of Demeter (Don't Talk to Strangers)

Overview

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.

When to Use

  • Accessing nested properties: obj.a.b.c
  • Chaining method calls: obj.getA().getB().getC()
  • Reaching through objects for data
  • Long dot chains in your code

The Iron Rule

NEVER chain through objects. Ask, don't reach.

No exceptions:

  • Not for "it's simpler"
  • Not for "it's just one chain"
  • Not for "the data is there"
  • Not for "fewer lines of code"

Detection: The Chain Smell

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();

The Correct Pattern: Ask, Don't Reach

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);

Why Chains Are Bad

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

Allowed Method Calls

A method m of class C should only call methods on:

  1. this - C's own methods
  2. Parameters - Objects passed to m
  3. Created objects - Objects m creates
  4. Components - C's direct instance variables
  5. Globals - Accessible global objects (sparingly)
class 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 Resistance Protocol

1. "It's Simpler"

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.

2. "It's Just One Chain"

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.

3. "The Data Is Right There"

Pressure: "The structure has the data, why wrap it?"

Response: Structure changes. Wrapping isolates you from changes.

Action: Ask the owner for the data.

4. "It's Read-Only"

Pressure: "I'm just reading, not modifying"

Response: Reading through chains still couples you to structure.

Action: Ask for what you need.

Red Flags - STOP and Reconsider

If you notice ANY of these, refactor:

  • Multiple dots: a.b.c.d
  • Chained getters: getA().getB().getC()
  • Optional chains: a?.b?.c?.d
  • Null checks for nested access
  • Structure knowledge in calling code
  • Mocking chains in tests

All of these mean: Add a method to ask directly.

Refactoring Chains

// ❌ 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();

Quick Reference

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()

Common Rationalizations (All Invalid)

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.

The Bottom Line

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.

You Might Also Like

Related Skills

coding-agent

coding-agent

179Kdev-codegen

Run Codex CLI, Claude Code, OpenCode, or Pi Coding Agent via background process for programmatic control.

add-uint-support

add-uint-support

97Kdev-codegen

Add unsigned integer (uint) type support to PyTorch operators by updating AT_DISPATCH macros. Use when adding support for uint16, uint32, uint64 types to operators, kernels, or when user mentions enabling unsigned types, barebones unsigned types, or uint support.

at-dispatch-v2

at-dispatch-v2

97Kdev-codegen

Convert PyTorch AT_DISPATCH macros to AT_DISPATCH_V2 format in ATen C++ code. Use when porting AT_DISPATCH_ALL_TYPES_AND*, AT_DISPATCH_FLOATING_TYPES*, or other dispatch macros to the new v2 API. For ATen kernel files, CUDA kernels, and native operator implementations.

skill-writer

skill-writer

97Kdev-codegen

Guide users through creating Agent Skills for Claude Code. Use when the user wants to create, write, author, or design a new Skill, or needs help with SKILL.md files, frontmatter, or skill structure.

Implements JavaScript classes in C++ using JavaScriptCore. Use when creating new JS classes with C++ bindings, prototypes, or constructors.

Creates JavaScript classes using Bun's Zig bindings generator (.classes.ts). Use when implementing new JS APIs in Zig with JSC integration.