tell-dont-ask

「Tell, Don't Ask」原則に基づくコードレビューと設計支援。オブジェクトの状態を問い合わせて 外部で判断するパターンを、オブジェクトに直接命じるパターンに変換する。カプセル化を強化し、 責任をデータを持つオブジェクトに集約する設計を促進する。コードレビュー、新規実装、 リファクタリング時にgetterの乱用やFeature Envyの改善が必要な場合に使用。 対象言語: Java, Kotlin, Scala, TypeScript, Python, Ruby, Go, Rust。 トリガー:「getterを減らしたい」「カプセル化を改善して」「Feature Envyを直して」 「オブジェクトに責任を持たせたい」「デメテルの法則」といったOOP設計関連リクエストで起動。

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "tell-dont-ask" with this command: npx skills add j5ik2o/okite-ai/j5ik2o-okite-ai-tell-dont-ask

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: カプセル化を破る必要がある場合の命名規約

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

domain-model-extractor

No summary provided by upstream source.

Repository SourceNeeds Review
General

cross-aggregate-constraints

No summary provided by upstream source.

Repository SourceNeeds Review
General

repository-design

No summary provided by upstream source.

Repository SourceNeeds Review
General

first-class-collection

No summary provided by upstream source.

Repository SourceNeeds Review