【第3部】 メソッドチェーンは危険信号?「デメテルの法則」で実現する疎結合なクラス設計

この記事は、保守性の高いコード設計シリーズの一部です。これまでの記事で、凝集度を高めるための様々なテクニックを見てきました。今回はその総仕上げとして、クラス間の「お作法」とも言える重要な設計原則を学びます。

こんにちは。皆さんは、object.getA().getB().getC().doSomething() のように、数珠つなぎにメソッドを呼び出すコードを書いたことはないでしょうか?これはメソッドチェーンと呼ばれ、時に「汽車事故(Train Wreck)」と揶揄される、危険な設計のサインです。

なぜなら、このようなコードは**デメテルの法則(The Law of Demeter)**という重要な設計原則を破っている可能性が高いからです。

目次

デメテルの法則と「尋ねるな、命じよ」

デメテルの法則は、非常にシンプルに言うと**「直接の友達とだけ話すべき(知らない人と話すべきではない)」**というルールです。オブジェクトは、自分が直接知っているオブジェクトのメソッドしか呼び出すべきではない、とされています。

user.getProfile().getAddress().getCity() というコードは、「userさん、あなたのprofileを教えて。…profileさん、あなたのaddressを教えて。…addressさん、あなたのcityを教えて」と、知らない相手に次々と尋ね回っているようなものです。

これは**「尋ねるな、命じよ (Tell, Don’t Ask)」**という、より本質的な原則にも反しています。オブジェクトからデータ(getProfile()など)を取り出して外部で処理するのではなく、そのデータをよく知るオブジェクト自身に処理を「命令」すべきなのです。

メソッドチェーンがもたらす問題

【Before】メソッドチェーンで、内部構造に深く依存したコード

// ユーザーの居住都市が送料無料の対象かチェックしたい
class ShippingManager {
    void applyFreeShipping(User user) {
        // userが持つProfile、Profileが持つAddress...と内部構造を深く知ってしまっている
        String city = user.getProfile().getAddress().getCity();

        if ("東京".equals(city)) {
            // 送料無料の処理
        }
    }
}

このコードの問題点は、ShippingManagerUserクラスだけでなく、ProfileクラスやAddressクラスの**内部構造にまで密接に依存(密結合)**してしまっていることです。

もし将来、Profileクラスの仕様が変わり、Addressを持たなくなったとしたらどうでしょう?ShippingManagerのコードは、直接関係ないはずの変更によって、修正を余儀なくされてしまいます。

解決策:オブジェクトに仕事を任せる

この問題を解決するには、ShippingManagerUserの内部を詮索するのをやめ、User自身に「送料無料の対象ですか?」と問い合わせるようにします。

【After】Userクラスに判断を「命令」する、疎結合なコード

// Userクラスに、自分自身に関する判断ロジックを持たせる(高凝集!)
class User {
    private final Profile profile;
    // ...

    // 「送料無料の対象地域に住んでいるか?」を判断する責務を負う
    boolean livesInFreeShippingArea() {
        String city = profile.getAddress().getCity();
        return "東京".equals(city);
    }
}

// ShippingManagerは、Userの内部構造を知る必要がなくなる
class ShippingManager {
    void applyFreeShipping(User user) {
        // Userに「尋ねる」のではなく、「判断して」と「命令」する
        if (user.livesInFreeShippingArea()) {
            // 送料無料の処理
        }
    }
}

UserクラスにlivesInFreeShippingAreaという責任あるメソッドを実装しました。これにより、ShippingManagerUserの内部構造(ProfileAddressを持っていること)を一切知る必要がなくなりました。

Userクラスの内部でどのように居住地を判断しているかは、Userクラス自身の問題です。将来、Addressの持ち方が変わっても、livesInFreeShippingAreaメソッドの振る舞いが同じであれば、ShippingManagerのコードには何の影響もありません。これこそがカプセル化がもたらす恩恵であり、疎結合な設計の力です。

まとめ

長いメソッドチェーンは、オブジェクトのカプセル化が破られ、ロジックが本来あるべき場所から漏れ出している危険なサインです。

  • 尋ねるな、命じよ: オブジェクトからゲッターでデータを取り出して処理するのではなく、そのオブジェクト自身に処理を実行させるメソッドを設計しましょう。
  • デメテルの法則を守る: オブジェクトは、その内部構造を外部に晒すべきではありません。必要な機能は、責任あるメソッドとして公開しましょう。

これらの原則を守ることで、クラス間の依存関係は適切に管理され、変更に強く、メンテナンスしやすい、真にオブジェクト指向なソフトウェアを構築することができるのです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次