メインコンテンツに移動

デメテルの法則とは?疎結合なソフトウェア設計を実現する原則を徹底解説

ソフトウェア開発では、オブジェクト同士の依存関係を適切に管理することが重要です。クラスやモジュールが必要以上に他の内部構造を知ってしまうと、少しの仕様変更でも多くの箇所に影響が広がり、保守しづらいコードになります。特に大規模なシステムでは、オブジェクト間の結合度が高くなるほど、修正コストやテストコストが大きくなります。

デメテルの法則は、オブジェクト間の結合度を下げるための設計原則です。簡単に言えば、「直接関係のある相手とだけやり取りし、遠くのオブジェクトの内部構造に依存しない」という考え方です。これは、「知らない人と話すな」「直接の友人とだけ会話せよ」と説明されることもあります。

この原則を守ることで、メッセージチェーンや深いオブジェクト参照を減らし、内部実装に依存しない設計を作りやすくなります。結果として、疎結合で保守しやすく、テストしやすいソフトウェアを実現できます。

本記事では、デメテルの法則の基本概念、メッセージチェーンや列車事故コードとの関係、カプセル化や委譲とのつながり、ソリッド原則、継承よりコンポジション、命令せよ・尋ねるなという設計思想、ウェブ開発やマイクロサービスでの活用まで体系的に解説します。

1. デメテルの法則とは?

デメテルの法則とは、オブジェクトが直接知っている相手とだけやり取りし、間接的な相手の内部構造に依存しないようにするための設計原則です。あるオブジェクトが、別のオブジェクトを通じてさらに奥のオブジェクトへアクセスするような設計は、結合度を高めやすくなります。そのため、直接の関係を持つ相手に処理を任せることが推奨されます。

この法則は、オブジェクト指向設計における疎結合を実現するための重要な考え方です。オブジェクトが他の内部構造を詳しく知りすぎると、依存関係が複雑になり、変更に弱い設計になります。デメテルの法則は、このような密結合を避け、責務を適切に配置するための指針になります。

主な特徴

項目内容
日本語名デメテルの法則
中心思想直接の関係を持つ相手とだけやり取りする
目的オブジェクト間の結合度を下げる
主な効果保守性・拡張性・テスト容易性の向上
関連概念カプセル化、委譲、情報隠蔽、メッセージチェーン

1.1 「知らない人と話すな」の意味

「知らない人と話すな」とは、オブジェクトが直接関係のない相手にアクセスしないという意味です。たとえば、あるクラスが別のクラスのプロパティを取得し、その中のさらに別のオブジェクトへアクセスし、その先の値を使って処理する場合、そのクラスは多くの内部構造を知りすぎています。

このような設計では、途中の構造が少し変わっただけで呼び出し側のコードも修正しなければならなくなります。デメテルの法則では、遠くのオブジェクトに直接アクセスするのではなく、直接の相手に必要な処理を依頼する設計を推奨します。

1.2 なぜ重要なのか

デメテルの法則が重要なのは、変更に強い設計を作るためです。オブジェクトが他の内部構造に依存していると、データ構造やクラス構成の変更が広い範囲へ波及します。これは保守性の低下につながります。

一方で、直接の相手に処理を任せる設計にすれば、内部構造を隠蔽できます。呼び出し側は細かい構造を知らなくても目的の処理を実行できるため、コードの見通しが良くなり、修正時の影響範囲も小さくなります。

2. デメテルの法則が生まれた背景

デメテルの法則が注目される背景には、オブジェクト指向設計における依存関係の複雑化があります。オブジェクト指向では、複数のクラスが互いに協力して処理を実行します。しかし、オブジェクト同士が必要以上に相手の内部構造を知ってしまうと、変更に弱い密結合な設計になります。

この問題を解決するために、オブジェクト同士のやり取りを必要最小限に抑える考え方が重要になりました。デメテルの法則は、オブジェクトが直接の関係を持つ相手とだけ会話し、間接的な依存を避けることで、保守性の高い構造を実現しようとする原則です。

2.1 オブジェクト指向設計の課題

オブジェクト指向設計では、クラスごとに責務を分け、オブジェクト同士がメッセージを送り合うことで処理を進めます。しかし、責務分離が不十分な場合、あるクラスが他のクラスの内部構造を深く知るようになり、依存関係が複雑になります。

このような状態では、クラスの独立性が失われます。たとえば、注文オブジェクトから顧客オブジェクトを取得し、さらに住所オブジェクトを取得し、その中の郵便番号を直接参照するようなコードは、注文、顧客、住所の構造に強く依存しています。これが後の保守を難しくします。

2.2 結合度の問題

結合度とは、ある部品が他の部品にどれだけ依存しているかを表す考え方です。結合度が高いほど、1つの変更が他の多くの部品に影響しやすくなります。逆に、結合度が低いほど、部品同士の独立性が高まり、変更やテストがしやすくなります。

デメテルの法則は、結合度を下げるための具体的な指針です。直接関係のあるオブジェクトだけに依存し、遠くのオブジェクトの内部構造を知らないようにすることで、変更の影響範囲を小さくできます。

2.3 保守性向上への取り組み

保守性の高いコードは、変更しやすく、理解しやすく、テストしやすいコードです。デメテルの法則は、オブジェクト同士の関係を整理し、不要な依存を減らすことで保守性を高めます。

特に長期運用されるシステムでは、最初に作ったクラス構造が何度も変更されることがあります。そのたびに多くの箇所を修正しなければならない設計は、開発効率を大きく下げます。デメテルの法則を意識することで、変更に強い設計を作りやすくなります。

3. デメテルの法則の基本ルール

デメテルの法則の基本ルールは、オブジェクトがやり取りする相手を限定することです。具体的には、自分自身、内部に持っているオブジェクト、メソッドの引数として受け取ったオブジェクト、自分で生成したオブジェクトなど、直接関係のある相手だけを利用する考え方です。

このルールを守ることで、遠くのオブジェクトに直接アクセスする必要がなくなります。結果として、内部構造への依存が減り、クラス間の結合度を低く保てます。

3.1 自分自身とだけやり取りする

まず基本となるのは、自分自身が持つメソッドや状態を使って処理することです。オブジェクトは、自分の責務に関係する処理を自分自身で行うべきです。他のオブジェクトの内部構造を探り、その情報を使って処理するのは避けるべきです。

自分自身で処理できる内容を外部へ漏らさないことで、カプセル化が強化されます。外部のクラスは、そのオブジェクトの内部構造を知らなくても、公開されたメソッドを通じて必要な処理を依頼できます。

3.2 直接の友人だけを利用する

デメテルの法則では、直接の友人だけを利用することが重要です。直接の友人とは、自分自身、メンバー変数、メソッド引数、自分で生成したオブジェクトなど、直接的な関係を持つ相手を指します。

直接の友人を通じてさらに別のオブジェクトへアクセスし続けると、依存関係が深くなります。たとえば、利用者から住所を取り出し、住所から地域を取り出し、地域から税率を取り出すような処理は、内部構造への依存が強くなります。こうした処理は、適切なオブジェクトに委譲する方が安全です。

3.3 間接的な依存を避ける

間接的な依存とは、あるオブジェクトを通じて、さらに奥にあるオブジェクトへ依存することです。これにより、呼び出し側は本来知らなくてもよい内部構造まで知ることになります。

間接的な依存を避けるには、必要な処理を直接の相手に任せる設計が有効です。たとえば、顧客の住所情報を外部から取り出して判断するのではなく、顧客オブジェクトに「配送可能か」を問い合わせるようにすれば、内部構造への依存を減らせます。

4. 「友人」とは?

デメテルの法則における「友人」とは、オブジェクトが直接やり取りしてよい相手を指します。すべてのオブジェクトと自由にやり取りしてよいわけではなく、自分が直接知っている範囲に限定することが重要です。

友人の範囲を明確にすることで、オブジェクト間の依存を整理できます。友人ではないオブジェクトに直接アクセスすると、内部構造への依存が強まり、変更に弱い設計になります。

4.1 自分自身

自分自身は、当然ながら直接利用してよい相手です。自分のメソッド、自分の状態、自分の責務に基づく処理は、自分自身の内部で完結できます。

ただし、自分自身に多くの責務を持たせすぎると巨大クラスになってしまいます。デメテルの法則は、外部への依存を減らす原則ですが、同時に責務の適切な分離も意識する必要があります。

4.2 メンバー変数

メンバー変数として保持しているオブジェクトも、直接の友人と見なされます。たとえば、注文サービスが注文リポジトリを持っている場合、注文サービスは注文リポジトリに処理を依頼できます。

ただし、メンバー変数を通じてさらに別の内部オブジェクトへ深くアクセスするのは避けるべきです。メンバー変数が提供する公開メソッドを利用し、その内部構造には踏み込まない設計が望まれます。

4.3 メソッド引数

メソッド引数として受け取ったオブジェクトも、直接の友人として扱えます。引数は、そのメソッドを実行するために呼び出し側から渡された直接の協力者だからです。

ただし、引数として受け取ったオブジェクトの内部構造を深くたどる設計は避けるべきです。引数のオブジェクトに必要な処理を依頼し、その結果を受け取る形にすれば、結合度を低く保てます。

5. なぜデメテルの法則が重要なのか

デメテルの法則が重要なのは、ソフトウェアの変更影響を小さくできるからです。オブジェクト同士が深く結びついていると、1つのクラスの内部構造を変更しただけで、多くの呼び出し側コードに修正が必要になります。

この法則を意識すると、各オブジェクトが直接の相手とだけ会話し、内部構造を隠蔽する設計になります。これにより、疎結合で保守しやすく、テストしやすいコードを作りやすくなります。

5.1 疎結合化

疎結合とは、部品同士の依存関係が弱く、互いの内部構造をあまり知らない状態です。デメテルの法則を守ると、オブジェクトが遠くの構造に依存しなくなるため、自然と疎結合な設計に近づきます。

疎結合なコードでは、あるクラスの内部実装を変更しても、外部への影響を抑えやすくなります。これは、長期的な保守性や拡張性を高めるうえで大きなメリットです。

5.2 保守性向上

保守性の高いコードは、変更箇所を特定しやすく、修正後の影響も把握しやすいコードです。デメテルの法則を守ると、オブジェクトの内部構造が外部へ漏れにくくなるため、修正範囲を限定しやすくなります。

たとえば、住所情報の内部構造を変更しても、外部の多くのクラスが住所の詳細構造に依存していなければ、修正範囲は小さく済みます。これは実務で非常に重要です。

5.3 変更影響の最小化

変更影響を最小化するには、内部構造への依存を避ける必要があります。デメテルの法則は、遠くのオブジェクトへ直接アクセスしないことで、変更影響を局所化します。

変更の影響範囲が小さければ、テスト範囲も絞りやすくなります。結果として、リリース時のリスクを下げ、継続的な改善を行いやすくなります。

6. メッセージチェーンとは?

メッセージチェーンとは、複数のメソッド呼び出しが連続してつながるコードのことです。たとえば、あるオブジェクトから別のオブジェクトを取得し、さらにその中の別のオブジェクトを取得するような書き方です。これは、デメテルの法則に違反しやすい代表的な構造です。

メッセージチェーンが長くなるほど、呼び出し側は多くの内部構造を知ることになります。その結果、途中の構造が変わるだけで呼び出し側のコードも壊れやすくなります。

6.1 長いメソッドチェーン

長いメソッドチェーンは、見た目には簡潔に見えることがあります。しかし、実際には複数のオブジェクト構造に依存しているため、保守性を低下させる原因になります。

たとえば、利用者からプロフィールを取得し、その中の住所を取得し、さらに郵便番号を取得するような処理は、呼び出し側が利用者、プロフィール、住所の構造をすべて知っている状態です。これは密結合を生みます。

6.2 深いオブジェクト参照

深いオブジェクト参照とは、オブジェクトの中にある別のオブジェクト、その中にあるさらに別のオブジェクトへアクセスすることです。これにより、呼び出し側は内部構造に強く依存します。

深い参照が増えると、データ構造の変更が難しくなります。本来は内部実装として隠したい構造が外部に漏れ出し、変更時に多くのコードへ影響します。

6.3 典型的な問題例

典型的な問題例は、画面表示や業務処理の中で、オブジェクトの内部構造を何段階もたどって値を取り出すコードです。このようなコードは、一見便利でも、後から構造変更に弱くなります。

改善するには、値を外部で取り出して判断するのではなく、適切なオブジェクトに処理を依頼することが重要です。たとえば、住所の詳細を外部で判断するのではなく、利用者オブジェクトに「配送可能か」を問い合わせる設計にできます。

7. メッセージチェーンが引き起こす問題

メッセージチェーンは、結合度の上昇、修正コストの増加、テスト難易度の上昇を引き起こします。呼び出し側が多くの内部構造を知るため、変更に弱いコードになります。

特に、複雑な業務システムや長期運用されるアプリケーションでは、メッセージチェーンが積み重なることで技術的負債になりやすいです。早めに責務を見直し、適切な委譲を行うことが重要です。

7.1 結合度の上昇

メッセージチェーンでは、呼び出し側が複数のオブジェクト構造に依存します。これは、単に1つのオブジェクトを利用しているのではなく、その内部構造全体に依存している状態です。

結合度が高くなると、変更時の影響範囲が広がります。途中のオブジェクト名や構造、戻り値が変わるだけで、呼び出し側のコードも修正しなければならなくなります。

7.2 修正コスト増加

メッセージチェーンが多いコードでは、内部構造の変更が難しくなります。たとえば、住所情報の保持場所を変更しただけでも、その住所情報を深く参照しているすべてのコードを修正する必要が出てきます。

このような修正は、直接の機能変更ではないにもかかわらず、多くの作業を発生させます。結果として、開発速度が低下し、バグ混入リスクも高まります。

7.3 テスト難易度上昇

メッセージチェーンがあると、テストの準備も複雑になります。深いオブジェクト構造を再現しなければならないため、テストデータやモックの作成が難しくなります。

テスト対象が本来必要としない内部構造まで知っている場合、単体テストが結合テストのように重くなります。デメテルの法則を守ることで、テスト対象の依存を減らし、テストしやすい構造を作れます。

8. 列車事故コードとは?

列車事故コードとは、メソッド呼び出しが列車の車両のように長く連結しているコードを指す表現です。点でつながった長い呼び出しが続く見た目から、このように呼ばれます。これは、メッセージチェーンの一種であり、デメテルの法則に違反しやすいコードです。

列車事故コードは、可読性や保守性を下げる原因になります。短く書けているように見えても、実際には多くの内部構造に依存しているため、変更に弱い設計になりやすいです。

8.1 列車事故コードの意味

列車事故コードとは、オブジェクトからオブジェクトへ次々にアクセスしていく長い呼び出しのことです。見た目としては、複数の呼び出しが連結され、1行に多くの依存関係が詰め込まれます。

このようなコードでは、呼び出し側が多くの内部構造を知っている状態になります。つまり、データの取得経路そのものに依存しており、途中の構造が変わると簡単に壊れます。

8.2 可読性への影響

列車事故コードは、読む側に多くの前提知識を要求します。呼び出しの途中にある各オブジェクトが何を返すのか、どの値が空になり得るのかを理解しなければなりません。

また、処理の意図が見えにくくなります。長い呼び出しで値を取り出しているだけでは、「何を判断したいのか」「どの責務がどこにあるのか」が分かりにくくなります。

8.3 保守性への影響

列車事故コードは、保守性に悪影響を与えます。内部構造の変更が呼び出し側に直接影響するため、修正範囲が広がります。また、空値や例外の扱いも複雑になりやすいです。

改善するには、内部構造をたどるのではなく、必要な判断や処理を適切なオブジェクトに委譲することが重要です。外部は「何をしたいか」を伝え、内部の詳細は対象オブジェクトに任せる設計が望まれます。

9. デメテルの法則を守る設計

デメテルの法則を守るには、責務を適切に配置し、処理を必要なオブジェクトに委譲し、外部に公開するインターフェースを整理することが重要です。単にメソッドチェーンを短くするだけではなく、オブジェクトの責務そのものを見直す必要があります。

良い設計では、外部のクラスが内部構造を知る必要がありません。必要な処理は、その情報を持っているオブジェクトに依頼します。これにより、内部構造を隠しながら、分かりやすい操作を提供できます。

9.1 適切な責務配置

デメテルの法則を守るには、まず責務の配置を見直す必要があります。あるクラスが他のオブジェクトから細かい情報を取り出して判断している場合、その判断は本来どのクラスが担当すべきかを考えるべきです。

情報を持っているオブジェクト自身が判断できるなら、そのオブジェクトにメソッドを追加する方が自然です。これにより、呼び出し側は内部データを知る必要がなくなり、結合度を下げられます。

9.2 委譲の活用

委譲とは、自分で処理を抱え込まず、適切なオブジェクトに処理を任せることです。デメテルの法則では、遠くのオブジェクトへ直接アクセスする代わりに、直接の相手へ処理を委譲する設計が重要になります。

委譲を活用すると、各オブジェクトの責務が明確になります。呼び出し側は「どうやって処理するか」ではなく「何をしてほしいか」を伝え、処理の詳細は委譲先に任せます。

9.3 インターフェース設計

デメテルの法則を守るには、外部に公開するインターフェースの設計も重要です。必要な操作を分かりやすいメソッドとして提供すれば、利用者は内部構造をたどる必要がなくなります。

たとえば、内部の複数オブジェクトを参照しなければ判断できない処理があるなら、その判断を行うメソッドを公開することで、呼び出し側の依存を減らせます。これは保守性の高いインターフェース設計につながります。

10. カプセル化との関係

デメテルの法則は、カプセル化と深く関係しています。カプセル化とは、オブジェクトの内部状態や実装詳細を外部から隠し、必要な操作だけを公開する考え方です。デメテルの法則は、このカプセル化を実践するための具体的な指針になります。

オブジェクトの内部構造を外部に公開しすぎると、外部コードがその構造に依存してしまいます。デメテルの法則を守ることで、内部実装を隠し、外部には必要な振る舞いだけを提供できます。

10.1 内部実装の隠蔽

内部実装の隠蔽とは、オブジェクトの内部構造を外部に見せないことです。外部のクラスが内部のフィールドや階層構造に依存しないようにすることで、変更に強い設計になります。

たとえば、住所の保持方法を変更しても、外部が住所の内部構造を知らなければ影響は小さくなります。外部は「配送可能か」「表示用住所を取得する」といった操作だけを利用すればよいのです。

10.2 情報隠蔽

情報隠蔽は、必要以上の情報を外部に公開しない設計原則です。デメテルの法則は、情報隠蔽を支える考え方でもあります。オブジェクトが直接の相手とだけやり取りすれば、遠くの内部情報に依存しなくなります。

情報隠蔽ができているコードでは、内部構造を変更しやすくなります。外部の利用者は公開された操作だけを使うため、内部の実装を自由に改善できます。

10.3 オブジェクトの独立性

カプセル化と情報隠蔽が適切に行われると、オブジェクトの独立性が高まります。独立性の高いオブジェクトは、他の内部構造に依存しないため、テストや再利用がしやすくなります。

デメテルの法則は、この独立性を保つために役立ちます。必要以上に他のオブジェクトを知りすぎないことで、各オブジェクトが自分の責務に集中できます。

11. オブジェクト指向設計との関係

デメテルの法則は、オブジェクト指向設計における基本的な品質を高めるための原則です。オブジェクト指向では、責務を持つオブジェクト同士が協力して処理を行います。その協力関係を適切に整理することが重要です。

オブジェクトが他の内部構造に依存しすぎると、オブジェクト指向の利点であるカプセル化や責務分離が弱くなります。デメテルの法則は、オブジェクト同士の関係を整理し、保守しやすい構造を作るために役立ちます。

11.1 責務の分離

責務の分離とは、各オブジェクトが自分の役割に集中することです。デメテルの法則を守ると、呼び出し側が他の内部情報を取り出して判断するのではなく、適切なオブジェクトに処理を任せる設計になります。

これにより、処理の責任が分かりやすくなります。どのクラスがどの判断を担当しているのかが明確になり、修正時にも迷いにくくなります。

11.2 モジュール化

モジュール化とは、システムを意味のある単位に分割することです。デメテルの法則を守ると、各モジュールが他の内部構造に依存しすぎないため、独立性の高いモジュールを作りやすくなります。

独立性の高いモジュールは、再利用やテストがしやすくなります。また、内部実装を変更しても外部への影響を抑えられるため、長期的な開発効率が向上します。

11.3 保守しやすい構造

保守しやすい構造とは、変更時の影響範囲が小さく、コードの意図が読み取りやすい構造です。デメテルの法則は、不要な依存を減らすことで、このような構造を実現します。

外部が内部構造を知らず、公開された操作だけを使う設計にすれば、クラスの内部を自由に改善できます。これは、リファクタリングしやすいコードを作るうえで重要です。

12. ソリッド原則との関係

デメテルの法則は、ソリッド原則とも深く関係しています。特に、単一責任の原則、依存性逆転の原則、オープン・クローズドの原則と相性が良い考え方です。これらの原則はいずれも、保守性や拡張性を高めるために依存関係を適切に管理することを重視します。

デメテルの法則は、ソリッド原則を実装レベルで支える指針として役立ちます。オブジェクトが直接の相手とだけ会話する設計にすることで、責務が整理され、抽象への依存や拡張しやすい構造を作りやすくなります。

12.1 単一責任の原則との関係

単一責任の原則は、1つのクラスが1つの責任だけを持つべきという考え方です。デメテルの法則を守ると、他のオブジェクトから情報を集めて判断する処理を減らし、責務を適切な場所へ移動しやすくなります。

あるクラスが多くのオブジェクトの内部情報を知っている場合、そのクラスは複数の責務を抱えている可能性があります。デメテルの法則は、このような責務の肥大化を発見する手がかりになります。

12.2 依存性逆転の原則との関係

依存性逆転の原則は、具象ではなく抽象に依存するべきという考え方です。デメテルの法則も、内部構造や具体実装への依存を避ける点で、この原則と相性があります。

直接の相手に対しても、具体的な内部構造に依存するのではなく、必要な振る舞いを表すインターフェースに依存することで、さらに柔軟な設計になります。これにより、実装の差し替えやテストがしやすくなります。

12.3 オープン・クローズドの原則との関係

オープン・クローズドの原則は、拡張には開き、修正には閉じるべきという考え方です。デメテルの法則を守ると、内部構造への依存が少なくなるため、新しい機能を追加しても既存コードへの影響を抑えやすくなります。

密結合なコードでは、機能追加のたびに多くの呼び出し側を修正する必要があります。疎結合な設計であれば、内部の変更や拡張を隠蔽しやすく、既存コードを安定させやすくなります。

13. 継承よりコンポジションとの関係

継承よりコンポジションという考え方は、継承による強い結合を避け、オブジェクトの組み合わせによって柔軟な設計を作る原則です。デメテルの法則も、疎結合な設計を重視する点でこの考え方と深く関係しています。

コンポジションを使う場合でも、内部に持っているオブジェクトのさらに奥へアクセスしすぎると、結局は密結合になります。そのため、コンポジションを使うだけでなく、適切な委譲と責務分離を行うことが重要です。

13.1 疎結合な設計

継承よりコンポジションでは、機能を部品として組み合わせることで柔軟な設計を作ります。デメテルの法則は、その部品同士の関係を必要以上に深くしないための指針になります。

コンポジションであっても、部品の内部構造を外部が深く知ってしまうと、疎結合とは言えません。部品の公開メソッドを通じて必要な処理を依頼し、内部構造は隠蔽することが重要です。

13.2 委譲の活用

コンポジションでは、内部に持つオブジェクトへ処理を委譲することが多くあります。デメテルの法則では、遠くのオブジェクトへ直接アクセスする代わりに、直接の相手に処理を任せることが推奨されます。

つまり、デメテルの法則とコンポジションは、委譲を通じてつながっています。適切な委譲を行えば、呼び出し側は内部構造を知らずに必要な処理を実行できます。

13.3 柔軟な拡張

デメテルの法則を守ったコンポジション設計では、内部構造を変更しても外部への影響を抑えられます。これにより、機能追加や実装変更がしやすくなります。

たとえば、通知処理の内部でメール、チャット、プッシュ通知を切り替える場合でも、呼び出し側がその内部構造を知らなければ、通知方式を追加しても外部コードを変更せずに済みます。

14. 命令せよ、尋ねるなとの関係

命令せよ、尋ねるなという考え方は、オブジェクトからデータを取り出して外部で判断するのではなく、オブジェクトに処理を命令するべきだという設計思想です。これは、デメテルの法則と非常に相性が良い考え方です。

データを外部へ取り出して判断すると、呼び出し側が内部構造を知る必要があります。一方、オブジェクトに処理を任せれば、内部構造を隠したまま目的を達成できます。

14.1 オブジェクトへの命令

命令せよ、尋ねるなでは、外部から細かい値を取得して判断するのではなく、オブジェクトに「これを実行してほしい」と依頼します。これにより、処理の責務がデータを持つオブジェクト側に集まります。

たとえば、注文の状態を外部で細かく調べてキャンセル可能か判断するのではなく、注文オブジェクトに「キャンセル可能か」や「キャンセルする」という操作を持たせる設計が考えられます。

14.2 データ取得の抑制

データ取得を抑制することは、デメテルの法則を守るうえで重要です。取得メソッドを多用すると、外部が内部状態を知り、その値に基づいて処理するようになります。

もちろん、すべての取得メソッドが悪いわけではありません。表示や単純な参照に必要な場合もあります。しかし、取得した値を使って複雑な判断を外部で行っている場合は、責務の配置を見直すべきです。

14.3 責務の明確化

命令せよ、尋ねるなを意識すると、責務が明確になります。データを持つオブジェクトが、そのデータに関する判断や操作を担当するため、処理の場所が自然になります。

これにより、外部のクラスが内部構造を知る必要がなくなります。結果として、デメテルの法則に沿った疎結合な設計になります。

15. リポジトリパターンとの関係

リポジトリパターンは、データアクセスを抽象化し、業務ロジックからデータ保存や取得の詳細を分離する設計パターンです。デメテルの法則とは、依存関係を整理し、内部実装への依存を減らす点で関係があります。

業務ロジックがデータベースの詳細構造や問い合わせ方法を直接知っていると、変更に弱い設計になります。リポジトリを通じてデータアクセスを行えば、業務ロジックは必要な操作だけを利用でき、内部の保存方法に依存しなくなります。

15.1 データアクセス抽象化

リポジトリパターンでは、データ取得や保存の処理をリポジトリに集約します。これにより、サービス層や業務ロジックは、データベースの詳細を知らずに必要なデータを取得できます。

これは、デメテルの法則における「遠くの内部構造を知らない」という考え方に近いです。業務ロジックがデータベースのテーブル構造や問い合わせ方法に直接依存しないため、保守性が向上します。

15.2 依存関係の整理

リポジトリを導入すると、依存関係が整理されます。サービス層はリポジトリの公開メソッドに依存し、リポジトリ内部の実装には依存しません。これにより、データアクセスの変更がサービス層へ直接影響しにくくなります。

ただし、リポジトリの戻り値が複雑な内部構造をそのまま外部に漏らしている場合は注意が必要です。リポジトリも、適切な抽象化と責務分離を意識して設計する必要があります。

15.3 保守性向上

リポジトリパターンを適切に使うと、データアクセスの変更に強くなります。データベースの種類や問い合わせ方法が変わっても、リポジトリ内部を修正すれば対応できる場合が多くなります。

デメテルの法則と組み合わせることで、サービス層がデータ構造を深く知りすぎることを防げます。結果として、業務ロジックとデータアクセスの境界が明確になり、保守しやすい構造になります。

16. ウェブ開発での活用

ウェブ開発では、制御層、サービス層、リポジトリ層、外部連携層など、複数の層が協力して処理を行います。デメテルの法則を意識することで、各層が他の内部構造に依存しすぎない設計を作れます。

特に、制御層がデータベース構造や外部連携の詳細を直接扱うような設計は避けるべきです。各層が自分の責務に集中し、必要な処理を適切な層へ委譲することが重要です。

16.1 サービス層設計

サービス層は、業務ロジックを担当する層です。デメテルの法則を守るには、サービス層が外部の内部構造を深く知りすぎないようにする必要があります。

たとえば、サービス層が利用者オブジェクトの中の住所の中の地域情報まで直接たどるのではなく、利用者や住所オブジェクトに必要な判断を任せる設計が望まれます。これにより、業務ロジックの見通しが良くなります。

16.2 制御層設計

制御層は、要求を受け取り、必要な処理へ振り分ける役割を持ちます。制御層に業務ロジックやデータ構造への深い依存を持たせると、肥大化しやすくなります。

制御層では、要求の受け取り、基本的な入力検証、サービス層への委譲、応答の返却に責務を限定するのが理想です。深いオブジェクト参照や複雑な判断は、適切なサービスやドメインオブジェクトへ移すべきです。

16.3 連携仕様アーキテクチャ

連携仕様アーキテクチャでは、外部に公開する操作を分かりやすく設計することが重要です。利用者側が内部構造を知らなくても目的を達成できるように、適切な粒度の機能を提供する必要があります。

デメテルの法則を意識すると、利用者側に内部データ構造をたどらせる設計を避けられます。必要な情報や操作を明確な連携仕様として提供することで、利用しやすく保守しやすい構造になります。

17. マイクロサービスでの活用

マイクロサービスでは、各サービスが独立した責務を持ち、連携仕様やイベントを通じて協力します。デメテルの法則は、サービス間の依存を抑え、各サービスの独立性を高めるためにも有効です。

サービス間で内部データ構造を深く共有しすぎると、あるサービスの変更が他のサービスへ大きく影響します。デメテルの法則を意識し、サービス境界を明確にすることが重要です。

17.1 サービス間依存削減

マイクロサービスでは、他サービスの内部構造に依存しないことが重要です。あるサービスが別サービスのデータ構造を細かく知っていると、その構造変更に弱くなります。

サービス間では、公開された連携仕様やイベントだけを通じてやり取りするべきです。内部のデータベース構造や実装詳細に依存しないことで、サービスごとの独立性を保てます。

17.2 連携仕様境界の明確化

連携仕様境界を明確にすることは、マイクロサービス設計の基本です。どのサービスがどの情報を提供し、どの操作を受け付けるのかを明確にすることで、不要な依存を防げます。

デメテルの法則を適用すると、他サービスの内部構造を直接たどるような連携を避けられます。必要な情報は、提供側サービスが適切な応答として返すべきです。

17.3 独立性向上

サービスの独立性が高まると、開発、テスト、配置、運用がしやすくなります。各サービスが他サービスの内部構造に依存しなければ、個別に変更しやすくなります。

デメテルの法則は、この独立性を支える考え方です。直接の契約だけに依存し、内部の詳細を知らない設計にすることで、サービス間の疎結合を実現できます。

18. デメテルの法則のメリット

デメテルの法則の主なメリットは、疎結合化、テスト容易性向上、拡張性向上です。内部構造への依存を減らすことで、変更に強いコードを作りやすくなります。

また、各オブジェクトの責務が明確になりやすいため、コードの理解もしやすくなります。結果として、長期的な保守コストを下げる効果が期待できます。

18.1 疎結合化

デメテルの法則を守る最大のメリットは、疎結合化です。オブジェクトが直接の相手とだけやり取りするため、遠くの内部構造に依存しなくなります。

疎結合な設計では、内部実装を変更しやすくなります。外部が内部構造を知らないため、実装を改善しても外部への影響を小さくできます。

18.2 テスト容易性向上

依存関係が少ないコードは、テストしやすくなります。深いオブジェクト構造を用意する必要がなくなり、テスト対象に必要な直接の協力者だけを準備すればよくなります。

また、責務が明確になることで、単体テストの目的も明確になります。どのクラスが何を保証するのかが分かりやすくなるため、品質保証にも役立ちます。

18.3 拡張性向上

デメテルの法則を守ると、内部構造への依存が減るため、機能追加や変更がしやすくなります。新しい処理を追加する場合でも、既存の呼び出し側が内部構造に依存していなければ、影響を抑えられます。

これは、長期運用されるシステムで特に重要です。仕様変更が続く環境では、拡張しやすい構造を最初から意識する必要があります。

19. デメテルの法則のデメリット

デメテルの法則には多くのメリットがありますが、過剰に適用するとデメリットもあります。委譲メソッドが増えたり、コード量が増加したり、設計が複雑に見える場合があります。

重要なのは、すべてのメソッドチェーンを機械的に排除することではありません。結合度や変更可能性を考慮し、本当に問題となる依存を見極めることが必要です。

19.1 委譲メソッドの増加

デメテルの法則を守るために、直接の相手へ処理を任せるメソッドを追加すると、委譲メソッドが増えることがあります。これにより、クラスのメソッド数が増え、単純な処理でもコード量が増える場合があります。

ただし、委譲メソッドが増えること自体が悪いわけではありません。そのメソッドが意味のある操作を表し、内部構造を隠す役割を持つなら、保守性向上に貢献します。

19.2 コード量の増加

内部構造を隠すためにメソッドを追加すると、短期的にはコード量が増えることがあります。単純に値を取り出すだけなら、直接参照の方が短く見える場合もあります。

しかし、短く書けることと保守しやすいことは同じではありません。変更が多い領域では、少しコード量が増えても、責務を明確にしておく方が長期的には有利です。

19.3 設計の複雑化

デメテルの法則を過剰に適用すると、どこまで委譲すべきか、どこで値を取得してよいかの判断が難しくなることがあります。結果として、設計が複雑に見える場合があります。

そのため、現実的なバランスが重要です。内部構造への依存が問題を生む箇所に重点的に適用し、単純なデータ参照まで過度に隠蔽しすぎない判断が求められます。

20. デメテルの法則を守らない例

デメテルの法則を守らない例としては、深いオブジェクト参照、過度な内部依存、密結合な設計が挙げられます。これらは、すぐにバグになるとは限りませんが、将来的な保守性低下の原因になります。

特に、画面表示や業務ロジックの中で、複数階層のオブジェクトを直接たどるコードが多い場合は注意が必要です。これは、内部構造への依存が外部に漏れているサインです。

20.1 深いオブジェクト参照

深いオブジェクト参照では、あるオブジェクトから別のオブジェクトを取得し、さらにその中の別オブジェクトへアクセスします。このようなコードは、途中の構造すべてに依存しています。

改善するには、必要な処理を適切なオブジェクトに移すことが重要です。呼び出し側が構造をたどるのではなく、対象オブジェクトに意味のあるメソッドを用意します。

20.2 過度な内部依存

過度な内部依存とは、外部のクラスが他のクラスの内部状態や構造に詳しく依存している状態です。これは、カプセル化が弱くなっているサインでもあります。

内部依存が強いと、実装を変更しづらくなります。外部が内部構造を前提にしているため、リファクタリング時に多くの箇所を修正する必要が出てきます。

20.3 密結合な設計

密結合な設計では、複数のクラスが互いに強く依存しています。あるクラスの変更が別のクラスに直接影響し、変更範囲が広がりやすくなります。

デメテルの法則は、この密結合を防ぐための考え方です。直接の相手とだけやり取りし、内部構造を隠蔽することで、結合度を低く保てます。

21. リファクタリングによる改善方法

デメテルの法則に違反しているコードは、リファクタリングによって改善できます。代表的な方法として、メソッド移動、責務再配置、カプセル化強化があります。

重要なのは、単にメソッドチェーンを短くすることではありません。なぜそのチェーンが必要になっているのかを考え、責務の置き場所を見直すことが大切です。

21.1 メソッド移動

メソッド移動とは、ある処理をより適切なクラスへ移すリファクタリングです。外部のクラスが他のオブジェクトの内部情報を使って判断している場合、その判断を情報を持つオブジェクトへ移すことができます。

これにより、呼び出し側は内部構造を知らなくて済みます。処理の責務が自然な場所に移動するため、コードの意図も分かりやすくなります。

21.2 責務再配置

責務再配置では、どのクラスがどの処理を担当すべきかを見直します。メッセージチェーンが多いコードでは、責務が呼び出し側に偏っている場合があります。

情報を持つオブジェクト、業務ルールを知るサービス、データアクセスを担当するリポジトリなど、役割に応じて処理を分けることで、設計が整理されます。

21.3 カプセル化強化

カプセル化強化とは、内部構造を外部に見せないようにすることです。単純に取得メソッドを減らすだけでなく、外部が必要とする操作を意味のあるメソッドとして提供します。

たとえば、外部が複数の内部値を取得して判断しているなら、その判断をオブジェクト内部に閉じ込めることができます。これにより、内部構造の変更に強い設計になります。

22. よくある設計ミス

デメテルの法則に関連するよくある設計ミスには、取得メソッドへの過度な依存、オブジェクト構造への依存、責務の分散があります。これらは、コードが動いていても保守性を下げる原因になります。

特に、値を取り出して外部で判断するコードが多い場合は注意が必要です。これは、オブジェクトに仕事を任せるのではなく、外部が内部情報を使って処理している状態だからです。

22.1 取得メソッド依存

取得メソッド依存とは、オブジェクトから値を取り出し、その値を使って外部で処理する設計です。取得メソッド自体は必要な場合もありますが、過度に使うと内部構造への依存が強くなります。

特に、取得した値を使って複雑な判断をしている場合は、オブジェクト側にその判断を移せないか検討すべきです。これは、命令せよ、尋ねるなという考え方にもつながります。

22.2 オブジェクト構造への依存

オブジェクト構造への依存とは、呼び出し側が内部の階層構造を前提にしている状態です。構造が変わると呼び出し側も壊れるため、変更に弱くなります。

この問題を避けるには、内部構造を直接公開せず、外部に必要な操作だけを提供することが重要です。構造ではなく振る舞いに依存する設計を目指すべきです。

22.3 責務の分散

責務の分散とは、本来1つのオブジェクトや層にまとめるべき処理が、複数の場所に散らばっている状態です。これにより、同じ判断がいろいろな場所で重複したり、仕様変更時に修正漏れが発生したりします。

デメテルの法則を意識すると、処理を適切な場所に集めやすくなります。データを持つオブジェクトや業務ルールを担当するサービスに責務を集約することで、保守性が向上します。

23. 実務でのベストプラクティス

実務でデメテルの法則を活用するには、オブジェクトに仕事を任せる、内部構造を公開しない、メッセージチェーンを避けるという基本を意識することが重要です。ただし、過剰適用は避け、現実的なバランスを取る必要があります。

特に、変更が多い領域、業務ルールが複雑な領域、外部連携が多い領域では、デメテルの法則を意識する効果が大きくなります。内部構造への依存を減らすことで、長期的に保守しやすいコードになります。

23.1 オブジェクトに仕事を任せる

外部からデータを取り出して判断するのではなく、オブジェクトに仕事を任せる設計を意識します。これは、デメテルの法則だけでなく、命令せよ、尋ねるなという設計思想にも合っています。

たとえば、外部で注文状態を判定するのではなく、注文オブジェクトに状態判定や状態変更を任せることで、責務が明確になります。呼び出し側は内部構造を知らずに済みます。

23.2 内部構造を公開しない

内部構造を公開しすぎると、外部がその構造に依存します。公開するのは、内部データそのものではなく、外部が必要とする意味のある操作にするべきです。

たとえば、内部の住所オブジェクトをそのまま渡すのではなく、表示用住所を返す、配送可能かを判定するなど、目的に合った操作を提供する方が保守しやすくなります。

23.3 メッセージチェーンを避ける

長いメッセージチェーンは、内部構造への依存が強いサインです。コードレビューでは、深い呼び出しがないかを確認し、必要に応じて責務を見直します。

ただし、すべてのチェーンを禁止する必要はありません。重要なのは、そのチェーンが内部構造への不要な依存を生んでいるかどうかです。問題がある場合は、委譲やメソッド移動で改善します。

24. デメテルの法則の限界

デメテルの法則は有用な設計原則ですが、万能ではありません。すべての依存を避けようとすると、委譲メソッドが増えすぎたり、コードがかえって分かりにくくなったりすることがあります。

そのため、現実の開発ではバランスが重要です。変更頻度、責務の明確さ、テスト容易性、可読性を考慮しながら、どこまで法則を適用するか判断する必要があります。

24.1 過剰適用の問題

デメテルの法則を過剰に適用すると、単純な値取得まで過度に隠蔽しようとして、不要なメソッドが増えることがあります。これにより、コード量が増え、かえって理解しづらくなる場合があります。

重要なのは、内部構造への依存が問題になるかどうかです。単純なデータ参照であり、変更リスクが低い場合には、過度な隠蔽が不要なこともあります。

24.2 バランスの重要性

設計原則は、機械的に守るものではなく、より良い設計判断をするための道具です。デメテルの法則も、疎結合化や保守性向上のために使うべきであり、コードを複雑にするために使うものではありません。

実務では、可読性、変更容易性、実装コストのバランスを考えます。特に、チーム全体が理解しやすい設計にすることが重要です。

24.3 現実的な設計判断

現実的な設計判断では、変更されやすい領域や業務ルールが複雑な領域を優先してデメテルの法則を適用します。一方で、単純なデータ転送や表示用の処理では、過度な抽象化を避けることもあります。

重要なのは、なぜその依存が必要なのかを説明できることです。依存関係に意図があり、変更時の影響も理解できているなら、現実的な設計判断として受け入れられる場合があります。

おわりに

デメテルの法則は、オブジェクト間の依存を最小限にするための設計原則です。「直接の友人とだけ会話する」という考え方を中心に、遠くのオブジェクトの内部構造に依存しない設計を目指します。これにより、疎結合で保守しやすいソフトウェアを実現しやすくなります。

この法則は、メッセージチェーンや列車事故コードを避けるうえで重要です。長いメソッドチェーンや深いオブジェクト参照は、内部構造への依存を強め、変更に弱いコードを生みます。適切な委譲や責務配置によって、これらの問題を改善できます。

また、デメテルの法則は、カプセル化、情報隠蔽、オブジェクト指向設計、ソリッド原則、継承よりコンポジション、命令せよ・尋ねるなという考え方とも密接に関係しています。単独のルールとしてではなく、保守性の高い設計を実現するための考え方として理解することが重要です。

ただし、デメテルの法則を過剰に適用すると、委譲メソッドが増えすぎたり、設計が複雑になったりする場合があります。実務では、変更頻度、責務の明確さ、可読性、テスト容易性を踏まえて、現実的なバランスを取る必要があります。

疎結合なシステムを作るためには、オブジェクトに仕事を任せ、内部構造を公開しすぎず、不要なメッセージチェーンを避けることが大切です。デメテルの法則を適切に活用することで、長期的に保守しやすく、拡張しやすいソフトウェア設計を実現できるでしょう。

LINE Chat