Talk only to your direct friends. Do not talk to strangers.
Core Principle
A method should call only the methods of its "direct friends" and should not reach for "friends of friends."
Proposed by Karl Liebherr (1987, Northeastern University). The formal name is the "Principle of Least Knowledge."
| Approach | Characteristic | Problem |
|---|---|---|
| Chained calls | a.getB().getC().doX() |
Depends on internal structure, fragile to change |
| Delegation | a.doX() |
Low coupling, resistant to change |
Four Rules
Method M may call only the following four kinds of methods:
| # | Allowed call targets | Description |
|---|---|---|
| 1 | Itself (this / self) method |
Methods defined in its own class |
| 2 | M Methods of objects passed as arguments to |
Direct friends via parameters |
| 3 | M Methods of objects created within |
Objects you created yourself are friends |
| 4 | Methods of its own instance variables (fields) | Objects you hold are friends |
Prohibited: Calling methods on thereturn valueof the above method calls (= friends of friends)
Decision Flow
メソッド内で obj.method() を呼んでいる
↓
obj はどこから来たか?
├─ this/self のフィールド → ✅ 許可(ルール4)
├─ メソッドの引数 → ✅ 許可(ルール2)
├─ メソッド内で new/生成した → ✅ 許可(ルール3)
├─ this/self 自身 → ✅ 許可(ルール1)
└─ 別のメソッド呼び出しの戻り値 → ❌ 違反(友人の友人)
Antipattern Detection
Train Wreck
A pattern that follows an object's internal structure through chained dot calls:
❌ order.getCustomer().getAddress().getCity()
❌ user.getProfile().getSettings().getTheme().getName()
❌ app.getConfig().getDatabase().getConnection().execute(query)
❌ invoice.getLineItems().get(0).getProduct().getCategory()
Detection Criteria
| Pattern | Problem |
|---|---|
| Chain of two or more dots | Structural dependency (though fluent APIs are exceptions) |
| Getter chain + operation at the end | Operating on a retrieved object = friend of a friend |
| Getter chain + if statement | Branching on the state of a distant object |
Transformation Patterns
1. Introduce a delegation method
// ❌ 違反: 友人(order)の友人(customer)の友人(address)に話しかけている
City city = order.getCustomer().getAddress().getCity();
// ✅ 修正: 各レベルに委譲メソッドを追加
City city = order.getShippingCity();
// Order
public City getShippingCity() {
return customer.getShippingCity();
}
// Customer
public City getShippingCity() {
return address.getCity();
}
2. Method names suited to the purpose
// ❌ 違反: 内部構造を露出した名前
Email email = order.getCustomer().getEmail();
// ✅ 修正: 目的を表すメソッドを提供
Email email = order.getNotificationEmail();
3. Pass as a parameter
// ❌ 違反: 遠いオブジェクトを取得して使用
void processOrder(Order order) {
PaymentGateway gateway = order.getCustomer().getPaymentGateway();
gateway.charge(order.getTotal());
}
// ✅ 修正: 必要なオブジェクトを引数で受け取る
void processOrder(Order order, PaymentGateway gateway) {
gateway.charge(order.getTotal());
}
4. Move behavior to the object
// ❌ 違反: 外部で判定
if (order.getCustomer().getAddress().getCountry().equals("JP")) {
applyJapaneseTax(order);
}
// ✅ 修正: 判定ロジックをオブジェクトに持たせる
Order orderUpdated = order.applyApplicableTax();
// Order
public Order applyApplicableTax() {
return customer.applyTaxFor(this);
}
Exceptions: Cases That Are Not Violations
1. Fluent API / Builder pattern
// ✅ 許可: 流暢APIは同一オブジェクトを返す
User user = User.builder()
.name("Alice")
.email("alice@example.com")
.build();
Reason: Because each method returns this , you are not talking to a friend of a friend but continuing to talk to the same friend.
2. Data structures (DTO / Value Object)
// ✅ 許可: DTOは振る舞いを持たない単なるデータ構造
City city = addressDto.getCity();
int zip = addressDto.getZipCode();
Reason: DTOs are not intended to hide internal structure. However, for domain objects it is a violation.
3. Stream / Collection Operations
// ✅ 許可: ストリームAPIの連鎖
List<String> names = users.stream()
.filter(u -> u.isActive())
.map(u -> u.getName())
.collect(Collectors.toList());
Reason: The Stream API is a type of fluent API, not an exploration of internal structure.
4. Standard Library Chaining
# ✅ 許可: 標準ライブラリの連鎖
result = text.strip().lower().replace(" ", "_")
Reason: This is method chaining on the same type (String), not a dependency on internal structure.
Warning on Overapplication
Beware of the "wrapper method explosion"
// 過剰: すべてのフィールドに委譲メソッドを作成
class Order {
Name getCustomerName() { return customer.getName(); }
Email getCustomerEmail() { return customer.getEmail(); }
Phone getCustomerPhone() { return customer.getPhone(); }
City getCustomerCity() { return customer.getAddress().getCity(); }
// ... 延々と続く
}
Countermeasure: Delegate only the operations that are truly necessary. Delegating all fields indicates a design problem.
Criteria for Judgment
| Situation | Response |
|---|---|
| 3 or fewer delegating methods | Appropriate |
| 4 or more delegating methods | Review the design (consider splitting responsibilities) |
| A large amount of delegation to the same delegate | Consider whether that object should be used directly |
Related Principles
| Principle | Relationship |
|---|---|
| Tell, Don't Ask | Complementary relationship. TDA says "tell", LoD says "only to friends" |
| Encapsulation | LoD is a means of structurally enforcing encapsulation |
| Loose Coupling | Adhering to LoD mechanically reduces coupling |
| Single Responsibility Principle | A wrapper method explosion is a sign of an SRP violation |
| breach-encapsulation-naming | A naming convention that explicitly restricts getters |
Review Perspectives
Checkpoints during code review:
- Dot chaining:
a.b().c()Are there chains of the form with 2 or more levels? - Getter chaining: Is there a pattern of get → get → operate?
- Dependency on distant objects: Is the method referencing objects it should not know about?
- Checking exceptions: Acceptable for fluent APIs / DTOs / streams
Detailed Guidelines
For language-specific implementation patterns and details of refactoring procedures, see references/patterns.md .
Related Skills (Recommended for Concurrent Reading)
When using this skill, also refer to the following skills:
tell-dont-ask: Complementary principle on the behavioral side (tell, don't ask about state)first-class-collection: Pattern for applying the Law of Demeter to collectionsbreach-encapsulation-naming: Naming conventions when chained calls cannot be avoided