SuperAPI AI Directory

tell-dont-ask

General & other

View repository

Updated 10-16 06:19

About this skill

Tell, Don't Ask

Don't ask an object, tell it.

Core Principles

Do not make decisions based on an object's internal state and then update that object with the result. (The Pragmatic Programmer, 2nd Edition, p. 167)

Approach Characteristics Problems
Ask Get state -> judge externally -> operate Logic is scattered, encapsulation is broken
Tell Command the object directly Responsibility is consolidated, resilient to change

Decision Flow

オブジェクトのメソッド呼び出し
    ↓
getterで状態を取得しているか?
    ├─ YES → その後ifで判定している?
    │         ├─ YES → Askパターン(問題あり)
    │         └─ NO → 表示/出力目的なら許容
    └─ NO → Tellパターン(推奨)

Anti-Pattern Detection

If you find the following patterns, consider converting them:

❌ if (obj.getX() > threshold) { obj.setY(...) }
❌ if (obj.getStatus() == ACTIVE) { doSomething(obj) }
❌ obj.getA().getB().doSomething()  // デメテルの法則違反
❌ for (item : list) { total += item.getPrice() }
❌ if (user.getRole() == ADMIN) { ... }

Conversion Patterns

1. Internalize state judgment

// ❌ Ask: 状態を取得して外部で判断
if (user.getAge() >= 18) {
    allowAccess(user);
}

// ✅ Tell: 判定ロジックをオブジェクトに持たせる
if (user.isAdult()) {
    allowAccess(user);
}

// ✅✅ さらに良い: 処理自体を委譲
user.ifAdult(() -> allowAccess());

2. Turn conditionals into polymorphism

// ❌ Ask: 型で分岐
if (user.getType() == UserType.ADMIN) {
    sendAdminNotification(user);
} else {
    sendUserNotification(user);
}

// ✅ Tell: 各クラスに責任を持たせる
user.sendNotification();  // Admin/RegularUserで実装が異なる

3. Delegate collection operations

// ❌ Ask: 外部で集計
int total = 0;
for (Item item : order.getItems()) {
    total += item.getPrice();
}

// ✅ Tell: オブジェクトに集計を任せる
int total = order.calculateTotal();

4. Null Object Pattern

// ❌ Ask: null判定の分岐
Address addr = user.getAddress();
if (addr != null) {
    return addr.format();
} else {
    return "住所未登録";
}

// ✅ Tell: NullObjectでデフォルト動作を定義
return user.getAddress().format();  // NullAddressは"住所未登録"を返す

Related Principles / Skills

Principle / Skill Relationship
law-of-demeter Avoid chained calls (a.getB().getC() → a.doC())
Feature Envy Obsession with another class's data -> move responsibility
Single Responsibility Principle Keep data and processing in the same place
Encapsulation Hide internal state and expose behavior
breach-encapsulation-naming Use getter naming to make encapsulation violations explicit

Application Guidelines

Recommended

  • Code that uses an if statement to judge after a getter
  • The same judgment logic is scattered across multiple places
  • Pattern of getting then updating an object's state
  • Conditional branching based on type or status

Avoid Overapplication

  • Data retrieval for display/reporting purposes
  • Simple value retrieval from a DTO/Value Object
  • When there are framework/library constraints
  • If the class becomes bloated, consider splitting responsibilities

Review Perspectives

Checkpoints during code review:

  1. getter + if: Is there conditional branching after getting state?
  2. Chained calls: a.getB().getC() Is there no such chain?
  3. External aggregation: Is data being collected in a loop?
  4. Type/status branching: Can it be replaced with polymorphism?

Detailed Guidelines

For language-specific implementation patterns and detailed refactoring procedures, see references/patterns.md .

Related Skills (Recommended Reading)

When using this skill, also refer to the following skills:

  • law-of-demeter: Complementary structural principle (talk only to your immediate friends)
  • first-class-collection: Patterns for applying Tell, Don't Ask to collections
  • breach-encapsulation-naming: Naming convention for when encapsulation needs to be broken