web-dev-qa-db-ja.com

各クラスが責任を1つだけ持つようにしてください、なぜですか?

Microsoftのドキュメントによると、ウィキペディアSOLIDプリンシペの記事、またはほとんどのITアーキテクトは、各クラスが1つの責任しか持たないことを確認する必要があります。理由を知りたいのです。ルールこのルールの理由について誰も同意しないようです。

メンテナンスの改善を指摘する人もいれば、簡単なテストを提供したり、クラスをより堅牢にしたり、セキュリティを強化したりする人もいます。何が正しいのですか、それは実際にはどういう意味ですか?なぜそれがメンテナンスを改善し、テストをより簡単にし、コードをより堅牢にするのですか?

37

モジュール性。適切な言語であれば、コードの一部を接着する手段は得られますが、プログラマーがソースの手術をせずに大きなコードの接着を解除する一般的な方法はありません。多くのタスクを1つのコード構造に詰め込むことで、自分や他の人が他の方法でその部分を組み合わせる機会を奪い、1つの部分を変更して他の部分に影響を与える可能性のある不要な依存関係を導入します。

SRPは、クラスと同様に関数にも適用できますが、主流のOOP言語は、関数同士を接着するのが比較的困難です。

57
Doval

保守の改善、テストの容易さ、バグ修正の迅速化は、SRPを適用した結果(非常に楽しい)です。主な理由(Robert C. Matinによると)は次のとおりです。

クラスには、変更する理由が1つだけ必要です。

言い換えると、SRPは変更の局所性を上げるです。

SRPはDRYコードも促進します。責任が1つしかないクラスがある限り、好きな場所でそれらを使用することを選択できます。2つの責任を持つクラスがある場合でも、そのうちの1つと2つ目が干渉している場合、2つのオプションがあります。

  1. クラスを別のクラスにコピーアンドペーストし、別の多責任ミュータントを作成することもできます(技術的な負債を少し増やします)。
  2. クラスを分割して、最初のクラスと同じように作成します。これは、元のクラスを頻繁に使用するために費用がかかる可能性があります。
30

特定の問題を修正するコードを簡単に作成できます。後で変更を安全に行えるようにしながら、その問題を修正するコードを作成するのはより複雑です。 SOLIDは、コードを改善する一連のプラクティスを提供します。

どちらが正しいか:3つすべて。これらはすべて、単一の責任を使用することの利点と、それを使用する必要がある理由です。

それらの意味について:

  • 優れたメンテナンスとは、変更が容易であり、頻繁に変更されないことを意味します。コードが少なく、そのコードは特定のものに焦点を当てているため、クラスに関連しない何かを変更する必要がある場合、クラスを変更する必要はありません。さらに、クラスを変更する必要がある場合は、パブリックインターフェイスを変更する必要がない限り、そのクラスについてのみ心配する必要があります。
  • 簡単なテストとは、テストの数が減り、セットアップが減ることを意味します。クラスには可動部分が少ないため、クラスで発生する可能性のある障害の数は少なくなり、テストする必要のあるケースが少なくなります。設定するプライベートフィールド/メンバーが少なくなります。
  • 上記の2つが原因で、変更が少なく失敗も少なく、したがってより堅牢なクラスが得られます。

原則に従ってしばらくの間コードを作成し、後でそのコードに戻って変更を加えてください。あなたはそれが提供する大きな利点を見るでしょう。

あなたは各クラスに同じことを行い、すべてがSRPに準拠するより多くのクラスになります。接続の観点から見ると、構造はより複雑になりますが、各クラスの単純さがそれを正当化します。

21
Miyamoto Akira

私の見解では、単一責任の原則は良い習慣であるという主張を支持する議論は次のとおりです。私はさらに詳細な推論を読むことができるさらなる文献へのリンクも提供します-そして私のものより雄弁です:

  • より良いメンテナンス:理想的には、システムの機能を変更する必要があるときはいつでも、変更する必要があるクラスは1つだけです。クラスと責任の間の明確なマッピングは、プロジェクトに関与する開発者がこれがどのクラスであるかを識別できることを意味します。 (@MaciejChałapukが指摘したように、 Robert C. Martin、 "Clean Code" を参照してください。)

  • より簡単なテスト:理想的には、クラスはできる限り最小限のパブリックインターフェイスを備え、テストはこのパブリックインターフェイスのみに対応する必要があります。クラスの多くの部分が非公開であるため、明確にテストできない場合、これはクラスに多くの責任があること、およびそれをより小さなサブクラスに分割する必要があることを示す明確な兆候です。これは、「パブリック」または「プライベート」クラスのメンバーがいない言語にも適用されることに注意してください。小さなパブリックインターフェイスを持つということは、クラスのどの部分を使用することになっているのかをクライアントコードから非常に明確にすることを意味します。 (詳細は Kent Beck、 "Test-Driven Development" を参照してください。)

  • 堅牢なコード:コードは適切に記述されているため、多かれ少なかれ失敗することはありません。しかし、すべてのコードと同様に、その最終的な目標は、マシンと通信することではなく、仲間の開発者と通信することです( Kent Beck、「実装パターン」を参照 、第1章)明確なコードベースのほうが推論しやすいため、導入されるバグは少なく、バグの発見から修正までの時間が短くなります。

4
logc

理由はいくつかありますが、私が好きなのは、初期のUNIXプログラムの多くで使用されているアプローチです。一つのことでそれを行うのは十分に困難であり、あなたがやろうとすることをより多くすることは難しくなります。

もう1つの理由は、副作用を制限および制御することです。私は私の組み合わせのコーヒーメーカーのドアのオープナーを追加しました。残念ながら、私が訪問者を迎えたとき、コーヒーは通常溢れていました。先日コーヒーを入れた後、ドアを閉めるのを忘れて誰かがそれを盗んだ。

心理的な観点から見ると、追跡できるのは一度にいくつかのことだけです。一般的な見積もりは7プラスまたはマイナス2です。クラスが複数のことを行う場合、それらすべてを一度に追跡する必要があります。これにより、実行していることを追跡する機能が低下します。クラスが3つのことを行い、そのうちの1つだけが必要な場合は、クラスで実際に何かを行う前に、物事を追跡する機能を使い果たす可能性があります。

複数のことを行うと、コードが複雑になります。最も単純なコードを超えて、複雑さが増すとバグの可能性が高まります。この観点からは、クラスをできるだけ単純にする必要があります。

1つのことを行うクラスをテストする方がはるかに簡単です。クラスが実行する2番目のこと、またはすべてのテストで発生しないことを確認する必要はありません。また、破損した状態を修正して、これらのテストの1つが失敗したときに再テストする必要はありません。

3
BillThor

ソフトウェアはオーガニックだからです。要件は常に変化するため、コンポーネントをできるだけ頭痛の少ない状態で操作する必要があります。 SOLID=の原則に従わないと、具体的に設定されたコードベースになってしまう可能性があります。

耐力コンクリート壁のある家を想像してみてください。この壁を何の支援もなく外すとどうなりますか?家はおそらく倒壊するでしょう。ソフトウェアではこれを望まないため、多くの損傷を与えることなくコンポーネントを簡単に移動、交換、変更できるようにアプリケーションを構成します。

2
CodeART

特に単一責任という重要な原則があるので、人々がこの原則を採用する理由はたくさんあると私は個人的に期待しています。

これらの理由のいくつかは次のとおりです。

  • メンテナンス-SRPは、1つのクラスの責任の変更が他の責任に影響を与えないようにし、メンテナンスを簡素化します。これは、各クラスに1つの責任しかない場合、1つの責任に対して行われた変更が他の責任から分離されるためです。
  • テスト-クラスに1つの責任がある場合、この責任をテストする方法を理解するのがはるかに簡単になります。クラスに複数の責任がある場合は、正しいものをテストしていることと、クラスが持つ他の責任の影響を受けないことを確認する必要があります。

また、SRPにはSOLID原則のバンドルが付属しています。SRPを遵守し、残りを無視することは、そもそもSRPを実行しないのと同じくらい悪いことです。したがって、SRPを単独で評価しないでください。しかし、SOLID原則のすべての文脈において。

1
Euphoric

私は考えに従います:1 Class = 1 Job

生理学の類推の使用:運動(神経系)、呼吸(肺)、消化器(胃)、嗅覚(観察)などそれぞれのサブシステムが機能する方法、またはエンドポイントサブシステムであり、指を離したり、毛包を成長させるなどの1つのタスクのみを実行するかどうか。

それが労働者の代わりにマネージャーとして行動しているかもしれないという事実を混同しないでください。一部のワーカーは、最終的にはManagerに昇格します。ワーカーが実行していた作業が複雑になり、1つのプロセスだけでは処理できない場合があります。

私が経験した中で最も複雑な部分は、クラスをオペレーター、スーパーバイザー、またはマネージャーのプロセスとして指定するタイミングを知ることです。いずれにしても、1つの責任(オペレーター、スーパーバイザー、またはマネージャー)について、その機能を観察して示す必要があります。

クラス/オブジェクトがこれらのタイプの役割を2つ以上実行すると、プロセス全体でパフォーマンスの問題、またはプロセスのボトルネックが発生し始めることがわかります。

1
GoldBishop

これらの原則の重要性を理解するための最良の方法は、必要があることです。

初心者のプログラマーだった頃は、あまりデザインを考えていませんでしたし、実際にデザインパターンが存在することすら知りませんでした。私のプログラムが成長するにつれ、1つのことを変更することは、他の多くのものを変更することを意味しました。バグを追跡するのは困難で、コードは膨大で反復的でした。オブジェクト階層はそれほど多くはなく、至る所にありました。 新しいものを追加したり、古いものを削除したりすると、プログラムの他の部分でエラーが発生します。図を参照してください。

小さなプロジェクトではそれは重要ではないかもしれませんが、大きなプロジェクトでは、物事は非常に悪夢になることがあります。後でデザインパターンの概念に出くわしたとき、私は自分に「ああ、そうすれば、物事はとても簡単になったでしょう」と自分に言い聞かせました。

必要になるまでは、デザインパターンの重要性を理解できません。私はパターンを尊重します。経験から、コードのメンテナンスが容易になり、コードが堅牢になるからです。

ただし、あなたと同じように、「簡単なテスト」についてはまだはっきりしていません。ユニットテストに参加する必要がまだないためです。

1
harsimranb

答えは、他の人が指摘したように、すべてが正しく、すべてが互いにフィードし合うため、テストが容易になり、メンテナンスが容易になり、コードがより堅牢になり、メンテナンスが容易になる、などです。

これらはすべて、主要なプリンシパルに要約されます。コードを小さくし、仕事を完了するために必要な処理を最小限に抑える必要があります。これは、関数と同様に、アプリケーション、ファイル、またはクラスに適用されます。コードの一部を実行するほど、理解、維持、拡張、またはテストが難しくなります。

私はそれを1つの単語、つまりスコープでまとめることができると思います。アーティファクトのスコープに細心の注意を払ってください。アプリケーションの特定の時点でスコープ内のものが少ないほど良いです。

スコープの拡大=より複雑=物事がうまくいかないためのより多くの方法。

1
jmoreno

クラスが大きすぎると、保守、テスト、および理解が困難になります。他の回答では、この意志をカバーしています。

クラスが問題なく複数の責任を持つことは可能ですが、複雑すぎるクラスではすぐに問題が発生します。

ただし、「責任は1つだけ」という単純なルールがあれば、それだけで済みます新しいクラスが必要なときに簡単に知ることができます

ただし、「責任」の定義は難しく、「アプリケーションの仕様にあることをすべて実行する」という意味ではありません。本当のスキルは、問題を「責任」の小さな単位に分割する方法を知っていることです。

1
Ian