web-dev-qa-db-ja.com

クラスが単一責任の原則を満たしているかどうかを判断するにはどうすればよいですか?

単一責任原則は、高い凝集性の原則に基づいています。 2つの違いは、SRPに準拠するクラスには1つの責任しかないが、凝集度の高いクラスには、関連性の高い一連の責任があるという点です。

しかし、特定のクラスが一連の責任を備えているために非常にまとまりがあるかどうか、または1つの責任しか持たず、したがってSRPを遵守しているかどうかをどのように判断しますか?言い換えれば、クラスが非常に細かいと考える人もいる(したがって、クラスがSRPに準拠していると信じる人もいる)ので、多かれ少なかれ主観的なものではないでしょうか。

34
user1483278

なぜそうなのかvery主観的であり、プログラマーが直面する多くの熱く真っ赤な議論の主題です。

答えは1つではありません。ソフトウェアが複雑になると、答えが変わる場合があります。かつては1つの明確に定義されたタスクであったものが、最終的には複数の不十分に定義されたタスクになる可能性があります。それも常に問題です。プログラムをタスクに分割する適切な方法をどのように選択しますか?

私が与えることができる唯一のアドバイスについては、これです:あなたの(そしてあなたの同僚の)最高の判断を使用してください。また、間違いをすぐに見つければ、(通常は)間違いを訂正できることを覚えておいてください。

21
Jason Baker

[〜#〜] solid [〜#〜] 原理を生み出したボブマーティン(ボブおじさん) [〜#〜] srp [〜#〜] が最初で、これについて言います(言い換えると、実際の単語を思い出せません):

クラスに変更する理由は1つだけです

複数の理由がある場合は、SRPに準拠していません。

13
Oded

いくつかの経験則をお話しします。

  • クラスに名前を付けるのは簡単ですか?クラスの名前を付けることが難しい場合は、多すぎます。
  • クラスにはパブリックメソッドがいくつありますか? 7 +/- 2は良い目安です。クラスにそれ以上の数がある場合は、クラスをいくつかのクラスに分割することを検討する必要があります。
  • 別のコンテキストで使用されるパブリックメソッドのまとまりのあるグループはありますか?
  • プライベートメソッドまたはデータメンバーはいくつありますか?クラスが複雑な内部構造を持っている場合は、おそらく内部を別の小さなクラスにパッケージ化するようにリファクタリングする必要があります。
  • そして最も簡単な経験則:クラスの大きさは?数百行を超える単一のクラスを含むC++ヘッダーファイルがある場合は、おそらくそれを分割する必要があります。
7
Dima

単一の責任原則では、各ソフトウェアモジュールは変更する理由が1つだけである必要があると述べています。最近の記事でボブおじさんは「変更する理由」を説明しました、

ただし、この原則について考えるとき、変更の理由は人であることを忘れないでください。変更を要求するのは人です。そして、さまざまな理由で多くの異なる人々が気にかけているコードを混ぜ合わせることで、それらの人々や自分自身を混乱させたくありません。

彼はさらに例 [〜#〜] here [〜#〜] で概念を説明しました。

6
theD

これに答えるには、一歩下がって、単一責任原則のintentionを検討してください。そもそもなぜそれが推奨される設計原則なのですか?

原則の目的は、コードベースを「コンパートメント化」することです。そのため、単一の「責任」に関連するコードは、単一のユニットに分離されます。これにより、コードの検索と理解が容易になり、さらに重要なのは、「責任」の変更がコードの単一ユニットにのみ影響することを意味します。

システムで絶対に望まないのは、小さなチャンスが原因で、コードの明らかに関連のない他の部分が失敗したり、動作が変わったりする場合です。 SRPは、バグと変更を分離するのに役立ちます。

では、「責任」とは何でしょうか。他の変化とは無関係に変化すると考えられるものです。いくつかの設定をXML構成ファイルに保存し、ファイルからそれらを読み取ることができるプログラムがあるとします。これは単一の責任ですか、それとも「ロード」と「保存」の2つの異なる責任ですか?ファイル形式や構造を変更する場合は、ロードロジックと保存ロジックの両方を変更する必要があります。したがって、それは単一のクラスによって表現されるべき単一の責任です。次に、一部のデータをCVS、Excel、およびXML形式でエクスポートできるアプリについて考えてみましょう。この場合、一方の形式が他方に影響を与えずに変更できることは容易に想像できます。 CVS形式の区切り文字を変更することを決定した場合、Excelの出力には影響しません。したがって、この場合は、3つの別個のクラス(同じインターフェースを共有すると思われる)で表す必要があります。

4
JacquesB

OOによると、クラスはデータの機能グループです。この定義は、主観的な解釈の余地をたくさん残しています。

クラスを明確かつ簡単に定義する必要があることはわかっています。ただし、そのようなクラスを定義するには、クラスが全体的な設計にどのように適合するかについて明確な概念が必要です。逆説的に、アンチパターンと見なされるウォーターフォールタイプの要件がないと、これを達成するのは困難です。

MVCのように、ほとんどの場合に機能するアーキテクチャでクラス設計を実装できます。 MVCアプリケーションでは、データ、ユーザーインターフェイス、および2つが通信するための要件のみを想定しています。

基本的なアーキテクチャでは、単一責任のルールが破られているケースを特定するのが簡単です。例えば。ユーザーコントロールのインスタンスをモーダルに渡す。

2
P.Brian.Mackey

あなたが言ったことに基づいて、具体的な方法を実行できます-高い凝集度が単一の責任を導き、凝集度を測定できます。最大凝集性クラスには、すべてのメソッドで使用されるすべてのフィールドがあります。最大のまとまりのあるクラスが常に可能であるとは限らず、これに到達することが依然として最善であるとは限らない。このクラス設計の目標を設定すると、クラスに多くのメソッドやフィールドを含めることができないと推測するのは非常に簡単です(多くて7つと言う人もいます)。

もう1つの方法は、OOP-実際のオブジェクトをモデルにしたモデルです。実際のオブジェクトの責任を確認するのははるかに簡単です。ただし、実際のオブジェクトが複雑すぎる場合は、複数に分割します。 containsgオブジェクトにはそれぞれ独自の責任があります。

1
m3th0dman

議論のために、私は [〜#〜] juce [〜#〜] からクラス AudioSampleBuffer を呼び出します。現在、このクラスは、オーディオのスニペット(またはおそらくかなり長いスニペット)を保持するために存在します。チャネル数、サンプル数(チャネルごと)を認識しているため、可変数値表現やワードサイズではなく、32ビットIEEE浮動小数点数にコミットされているように見えます(ただし、これは私には問題ありません)。 numChannelsまたはnumSamplesと特定のチャネルへのポインターを取得できるメンバー関数があります。 AudioSampleBufferを長くしたり短くしたりできます。前者はバッファをゼロ詰めし、後者は切り捨てると思います。

このクラスには、JUCEが使用する特別なヒープにスペースを割り当てるために使用されるプライベートメンバーがいくつかあります。

しかし、これはAudioSampleBufferが欠けているものです(そして私はそれについてJulesと何度か話し合いました):SampleRateというメンバー。 どのようにそれが欠けているのでしょうか?

AudioSampleBufferが果たす必要のある単一の責任は、サンプルが表すと聞く物理的なオーディオを適切に表すことです。サウンドファイルを読み取るものから、またはストリームからAudioSampleBufferを入力する場合、追加のパラメーターを取得して、AudioSampleBufferと一緒にサンプルレートを知る必要がある処理メソッド(たとえば、フィルター)に渡す必要があります。または、最終的に、バッファがを再生して再生される(またはそれを別の場所にストリーミングする)メソッドに。なんでも。

しかし、あなたがしなければならないことは、このSampleRateを渡し続けることです。これは、AudioSampleBufferに存在する特定のオーディオに固有であり、あらゆる場所に渡ります。 constant44100.0fが関数に渡されたコードを見たことがあります。プログラマーが他に何をすべきかを知らなかったからです。

これは、単一の責任を果たせなかった例です。