mcpField
T
tell-dont-ask

tell-dont-ask

github.com/j5ik2oDevelopment8037 installsUpdated 27w ago
Repository

About tell-dont-ask

tell-dont-ask is a skill for object-oriented design and code review that explains how to place decisions and operations inside the objects that own the relevant state. It covers anti-pattern detection, refactoring approaches, and limits on where the principle applies.

Key features

  1. 01Spot getters followed by conditional logic
  2. 02Move state decisions into objects
  3. 03Replace type branches with polymorphism
  4. 04Delegate collection operations and totals
  5. 05Provide code review checks

Use cases

  1. 01Review code that reads state before updating objects
  2. 02Refactor repeated status or type checks
  3. 03Reduce reliance on other objects' data

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/j5ik2o/okite-ai --skill tell-dont-ask

After installing, type/tell-dont-askin Claude Code to confirm it loaded.

Bundled filesreferences/patterns.md

Details

From the project README

Tell, Don't Ask

オブジェクトに問い合わせるな、命じよ。

核心原則

オブジェクトの内部状態に基づく意思決定をし、その結果で該当オブジェクトを更新してはならない。 (『達人プログラマー 第2版』167ページ)

アプローチ特徴問題
Ask状態を取得→外部で判断→操作ロジックが散在、カプセル化破壊
Tellオブジェクトに直接命じる責任集約、変更に強い

判断フロー

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

アンチパターン検出

以下のパターンを見つけたら変換を検討:

❌ 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) { ... }

変換パターン

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

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

// ✅✅ さらに良い: 処理自体を委譲
user.ifAdult(() -> allowAccess());
2. 条件分岐のポリモーフィズム化
// ❌ Ask: 型で分岐
if (user.getType() == UserType.ADMIN) {
    sendAdminNotification(user);
} else {
    sendUserNotification(user);
}

// ✅ Tell: 各クラスに責任を持たせる
user.sendNotification();  // Admin/RegularUserで実装が異なる
3. コレクション操作の委譲
// ❌ Ask: 外部で集計
int total = 0;
for (Item item : order.getItems()) {
    total += item.getPrice();
}

// ✅ Tell: オブジェクトに集計を任せる
int total = order.calculateTotal();
4. Nullオブジェクトパターン
// ❌ Ask: null判定の分岐
Address addr = user.getAddress();
if (addr != null) {
    return addr.format();
} else {
    return "住所未登録";
}

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

関連原則・スキル

原則 / スキル関係
law-of-demeter連鎖呼び出しを避ける(a.getB().getC() → a.doC())
Feature Envy他クラスのデータに執着 → 責任を移動
単一責任原則データと処理を同じ場所に
カプセル化内部状態を隠蔽し振る舞いを公開
breach-encapsulation-naminggetter命名でカプセル化破壊を明示

適用指針

推奨
  • getter後にif文で判定しているコード
  • 同じ判定ロジックが複数箇所に散在
  • オブジェクトの状態を取得→更新するパターン
  • 型やステータスによる条件分岐
過剰適用を避ける
  • 表示/レポート目的のデータ取得
  • DTO/Value Objectからの単純な値取得
  • フレームワーク/ライブラリの制約がある場合
  • クラスが肥大化する場合は責任分割を検討

レビュー観点

コードレビュー時の確認ポイント:

  1. getter + if: 状態取得後に条件分岐していないか
  2. 連鎖呼び出し: a.getB().getC() のようなチェーンはないか
  3. 外部での集計: ループでデータ収集していないか
  4. 型/ステータス分岐: ポリモーフィズムで置換できないか

詳細ガイドライン

言語別の実装パターン、リファクタリング手順の詳細は references/patterns.md を参照。

関連スキル(併読推奨)

このスキルを使用する際は、以下のスキルも併せて参照すること:

  • law-of-demeter: 構造面の補完原則(直接の友人とのみ会話する)
  • first-class-collection: コレクションへのTell, Don't Ask適用パターン
  • breach-encapsulation-naming: カプセル化を破る必要がある場合の命名規約

Comments

Markdown supported