単一責任の原則は、高い結束の原則に基づいています。この2つの違いは、SRPに準拠するクラスの責任は1つだけであるのに対し、非常に凝集性の高いクラスには密接に関連する一連の責任があることです。
しかし、特定のクラスに一連の責任があり、したがって非常に凝集性があるのか、それとも1つの責任しか持たず、したがってSRPに準拠するのかをどのように判断するのでしょうか。言い換えれば、クラスが非常にきめ細かいと考える人もいるかもしれません(クラスがSRPに準拠していると考える人もいるかもしれませんが)
単一責任の原則は、高い結束の原則に基づいています。この2つの違いは、SRPに準拠するクラスの責任は1つだけであるのに対し、非常に凝集性の高いクラスには密接に関連する一連の責任があることです。
しかし、特定のクラスに一連の責任があり、したがって非常に凝集性があるのか、それとも1つの責任しか持たず、したがってSRPに準拠するのかをどのように判断するのでしょうか。言い換えれば、クラスが非常にきめ細かいと考える人もいるかもしれません(クラスがSRPに準拠していると考える人もいるかもしれませんが)
回答:
はい、それは非常に主観的であり、プログラマーが入る多くの白熱した議論の対象です。
答えは1つではなく、ソフトウェアがより複雑になると答えが変わる可能性があります。かつて単一の明確に定義されたタスクであったものが、最終的には複数の不十分に定義されたタスクになる可能性があります。それは常に摩擦です。プログラムをタスクに分割する適切な方法をどのように選択しますか?
私が与えることができる唯一のアドバイスについてはこれです:あなた(そしてあなたの同僚)の最高の判断を使用してください。そして、間違いをすぐに見つければ(通常)修正できることを忘れないでください。
いくつかの経験則を説明できます。
単一責任原則では、各ソフトウェアモジュールには変更する理由が1つだけあるべきであると述べています。ボブおじさんが最近の記事で「変更する理由」を説明し、
ただし、この原則について考えると、変更の理由は人であることに注意してください。変更を要求するのは人々です。そして、多くの異なる人々がさまざまな理由で関心を持っているコードを混ぜることによって、それらの人々またはあなた自身を混乱させたくありません。
さらに彼は、一例で概念を説明HERE。
これに答えるには、一歩下がって、単一の責任原則の意図を考慮してください。そもそもなぜそれが推奨される設計原則なのですか?
原則の目的は、コードベースを「区分化」することであるため、単一の「責任」に関連するコードは単一のユニットに分離されます。これにより、コードの検索と理解が容易になります。さらに重要なことは、「責任」の変更がコードの単一ユニットにのみ影響することを意味します。
システムで絶対に必要としないのは、1つの小さなチャンスによって、コードの明らかに無関係な他の部分が失敗したり、動作が変更されたりする場合です。SRPは、バグと変更を分離するのに役立ちます。
それでは、「責任」とは何でしょうか?これは、他の変更とは独立して変更される可能性があるものです。いくつかの設定をXML構成ファイルに保存し、ファイルからそれらを読み戻すことができるプログラムがあるとします。これは単一の責任ですか、それとも「ロード」と「保存」の2つの異なる責任ですか?ファイル形式または構造に何らかの変更を加えるには、ロードロジックと保存ロジックの両方を変更する必要があります。したがって、単一のクラスによって表されるべき単一の責任です。次に、一部のデータをCVS、Excel、およびXML形式でエクスポートできるアプリを検討します。この場合、1つの形式が他の形式に影響を与えることなく変更できることは容易に想像できます。CVS形式で区切り文字を変更する場合、Excel出力には影響しません。
OOは、クラスはデータと機能のグループであると言います。この定義には、主観的な解釈の余地が十分にあります。
クラスは明確かつ簡単に定義する必要があることを知っています。しかし、そのようなクラスを定義するには、クラスが設計全体にどのように適合するかという明確な概念が必要です。逆説的に、アンチパターンと見なされるウォーターフォールタイプの要件がなければ、これを達成することは困難です。
MVCのようなほとんどの場合に機能するアーキテクチャを使用して、クラスデザインを実装できます。MVCアプリケーションでは、データ、ユーザーインターフェイス、および両者が通信するための要件のみを持っていると想定しています。
基本的なアーキテクチャにより、単一責任ルールが破られているケースを簡単に特定できます。EGユーザーコントロールのインスタンスをモーダルに渡す。
議論のために、AudioSampleBufferと呼ばれるJUCEのクラスを取り上げます。現在、このクラスは、オーディオのスニペット(またはかなり長いスニペット)を保持するために存在します。チャネル数、サンプル数(チャネルごと)がわかっているので、変数の数値表現やワードサイズではなく、32ビットIEEE floatにコミットされているようです(ただし、これは問題ではありません)。numChannelsまたはnumSamplesおよび特定のチャンネルへのポインターを取得できるメンバー関数があります。AudioSampleBufferを長くしたり短くしたりできます。前者はバッファをゼロで埋め、後者は切り捨てると仮定します。
JUCEが使用する特別なヒープにスペースを割り当てるために使用されるこのクラスのプライベートメンバーがいくつかあります。
しかし、これはAudioSampleBufferが欠けているものです(そして、私はJulesとそれについていくつかの議論をしました):と呼ばれるメンバーSampleRate
。 どうしてそれが欠けているのでしょうか?
AudioSampleBufferが果たす必要のある唯一の責任は、サンプルが表す物理的なオーディオを適切に表すことです。サウンドファイルまたはストリームから読み取るものからAudioSampleBufferを入力する場合、AudioSampleBufferとともに取得してサンプルレートを知る必要がある処理メソッド(フィルターなど)に渡す必要がある追加のパラメーターがあります。最終的には、バッファを再生して聞く(または他の場所にストリームする)メソッドに。なんでも。
しかし、あなたがしなければならないことは、このSampleRateを渡し続けることです。定数 44100.0fが関数に渡されるコードを見てきました。これは、プログラマが他に何をすべきかを知らなかったためです。
これは、単一の責任を果たさない例です。
あなたが言ったことに基づいて、具体的な方法を行うことができます-高い凝集性は、凝集性を測定できる単一の責任を導きます。最大の凝集クラスには、すべてのメソッドで使用されるすべてのフィールドがあります。最大の凝集クラスは、常に可能であるとは限らず、望ましいことでもありませんが、これに到達するのが最善です。このクラスの設計目標を設定すると、クラスに多くのメソッドやフィールドを含めることはできないと推測するのは非常に簡単です(せいぜい7個)。
別の方法は、OOPの純粋な基本からです-実際のオブジェクトをモデルにします。実際のオブジェクトの責任をより簡単に確認できます。ただし、実際のオブジェクトが複雑すぎる場合、複数の包含オブジェクトに分割し、それぞれに独自の責任を持たせます。