単一責任の原則-私はそれを過度に使用していますか?


13

参考-http://en.wikipedia.org/wiki/Single_responsibility_principle

アプリケーションの1つのモジュールで元帳エントリを作成するテストシナリオがあります。実行できる3つの基本的なタスクがあります-

  • 既存の元帳エントリを表形式で表示します。
  • [作成]ボタンを使用して新しい元帳エントリを作成します。
  • テーブルの元帳エントリ(最初のポインタで説明)をクリックして、次のページで詳細を表示します。このページの元帳エントリを無効にすることができます。

(各ページにはさらにいくつかの操作/検証がありますが、簡潔にするためにこれらに限定します)

そこで、3つの異なるクラスを作成することにしました-

  • LedgerLandingPage
  • CreateNewLedgerEntryPage
  • ViewLedgerEntryPage

これらのクラスはそれらのページで実行できるサービスを提供し、Seleniumテストはこれらのクラスを使用して、アプリケーションを特定のアサーションを作成できる状態にします。

私が同僚と一緒にそれをレビューしていたとき、彼は圧倒され、すべてのために1つのクラスを作るように頼まれました。まだデザインがきれいだと感じていますが、単一責任の原則を使いすぎているのではないかと疑っています


2
私見では、2つのことを知らずに、一般的にあなたの質問に答えようとする意味はありません。そして、近い将来、どれほど大きく/複雑になると予想されますか?
ペテルトレック

2
それぞれ10個のメソッドは、コードを30個のメソッドを持つ1つのクラスに入れないことを明確に示しています。
Doc Brown

6
これは境界領域です。100LOCと10メソッドのクラスは小さすぎず、300 LOCの1つは大きすぎませ。ただし、1つのクラスの30個のメソッドは私にはあまりにも聞こえます。全体的に、単一の太りすぎのクラスを持つよりも多くの小さなクラスを持つ方がリスクが少ないという点で、@ Docに同意する傾向があります。特に...クラスは自然に時間をかけて体重を得るために傾向があることを考慮に入れて
ペーテルTörök

1
@Pet
SoylentGray

1
おそらくMVCを適用すると、これがすべてクリアされます。3つのビュー、1つのモデル(元帳)、そしておそらく3つのコントローラーです。
ケビンクライン

回答:


19

ここで @YannisRizosを引用:

これは本能的なアプローチであり、変更する可能性がより高いのは元帳を管理するビジネスロジックであるため、おそらく正しいでしょう。その場合、3つのクラスよりも1つのクラスを維持する方がはるかに簡単です。

少なくとも今、約30年のプログラミング経験の後、私は同意しません。小さいクラスのIMHOは、このようなケースで考えることができるほとんどすべてのケースで維持する方が適切です。1つのクラスに追加する関数が多いほど、構造が少なくなり、コードの品質が低下します-毎日少しずつ。Tarunを正しく理解したら、これら3つの操作のそれぞれ

LedgerLandingPage

CreateNewLedgerEntryPage

ViewLedgerEntryPage

はユースケースであり、これらの各クラスは関連タスクを実行する複数の関数で構成され、各クラスは個別に開発およびテストできます。したがって、それらのそれぞれにクラスを作成すると、一緒に属するものがグループ化され、何かが変更される場合に変更されるコードの部分を識別するのがはるかに簡単になります。

これらの3つのクラスに共通の機能がある場合、それらは共通の基本クラス、Ledgerクラス自体(少なくともCRUD操作用に1つあると仮定)または別のヘルパークラスに属します。

多数の小規模なクラスを作成するためにより多くの引数が必要な場合は、Robert Martinの本「Clean Code」をご覧になることをお勧めします。


これが、すべてを1つのクラスにプッシュするのではなく、3つのdiffクラスを思いついた理由です。これらのいずれにも共通の機能はないため、共通のクラスはありません。リンクありがとうございます。
タルン

2
「私が考えることができるほとんどすべての場合において、より小さなクラスを維持するほうが良いです。」Clean Codeでさえそこまで行かないと思います。たとえば、MVCパターンでは、ページごとに1つのコントローラー(または、ユースケース)が必要であると本当に主張しますか?リファクタリングでは、ファウラーには2つのコード臭があります。1つはクラスを変更する多くの理由(SRP)を参照し、もう1つは1つの変更で多くのクラスを編集する必要があることを示します。
pdr

@ pdr、OPはこれらのクラス間に共通の機能がないと述べているため、(私にとって)単一の変更を行うために複数の操作を行う必要がある状況を想像するのは困難です。
ペテルトレック

@pdr:1つの変更に対して編集するにはクラスが多すぎる場合、それは主に、共通の機能を別の場所にリファクタリングしなかったサインです。しかし、上記の例では、これはおそらく1つだけではなく、4番目のクラスにつながります。
Doc Brown

2
@pdr:ところで、あなたは実際に「クリーンコード」を読みましたか?ボブ・マーティンは、コードを小さな単位に分解することに非常に力を入れています。
Doc Brown

11

Single Responsibility Principle、またはその他の原則の決定的な実装はありません。変更する可能性が高いものに基づいて決定する必要があります。

@Karpieが以前の回答で書いているように

必要なクラスは1つだけで、そのクラスの責任は元帳の管理です。

これは本能的なアプローチであり、変更する可能性がより高いのは元帳を管理するビジネスロジックであるため、おそらく正しいでしょう。その場合、3つのクラスよりも1つのクラスを維持する方がはるかに簡単です。

しかし、それはケースごとに調べて決定する必要があります。変更の可能性がごくわずかであるという懸念を実際に気にせず、変更の可能性が高いものに原則を適用することに集中するべきではありません。元帳を管理するロジックが多層プロセスであり、レイヤーが独立して変更される傾向がある場合、または原則としてロジック(プレゼンテーション)で分離する必要がある場合は、別個のクラスを使用する必要があります。それは確率のゲームです。

設計原則の使いすぎに関する同様の質問です。興味深いと思うと思います。設計パターン:使用するタイミングと、パターンを使用してすべてをやめるタイミング


1
私の理解では、SRPは実際には設計パターンではありません。
Doc Brown

もちろんないデザインパターンの@DocBrownが、問題の議論は非常に関連性がある...良いキャッチけれども、私は答えを更新しました
ヤニス

4

これらのクラスを調べてみましょう。

  • LedgerLandingPage:このクラスの役割は、元帳のリストを表示することです。アプリケーションが成長するにつれて、おそらく他のデータソースからのデータをソート、フィルタリング、ハイライトし、透過的に融合するためのメソッドを追加する必要があります。

  • ViewLedgerEntryPage:このページには、レジャーの詳細が表示されます。かなり簡単そうです。データが単純である限り。ここで元帳を無効にすることができます。修正することもできます。または、コメントするか、ファイル、領収書、外部予約番号などを添付してください。また、編集を許可したら、間違いなく履歴を表示する必要があります。

  • CreateLedgerEntryPage:にViewLedgerEntryPage完全な編集機能がある場合、このクラスの責任は、「空のエントリ」を編集することで満たすことができます。

これらの例は少し遠いように見えるかもしれませんが、ポイントは、UXからここで機能を話しているということです。単一の機能は間違いなく明確な単一の責任です。その機能の要件は変化する可能性があり、2つの異なる機能の要件は2つの反対方向に変化する可能性があるため、最初からSRPを使い続けたいと望んでいるからです。
ただし、逆に、考えられる理由が何であれ、1つのインターフェイスを使用する方が便利な場合は、いつでもファサードを3つのクラスに平手打ちできます。


SRPの過剰使用などがあります。ファイルの内容を読み取るために12個のクラスが必要な場合は、それだけをしていると想定する方が安全です。

反対側からSRPを見ると、実際には、1つのクラスが1つの責任を引き受ける必要があると言われています。ただし、責任は抽象化レベル内にのみ存在することに注意してください。したがって、より高い抽象化レベルのクラスが、作業を下位レベルのコンポーネントに委任することによってその責任を遂行することは完全に理にかなっています


これらのクラスの責任をあなたが説明したよりもうまく説明できなかったと思います。
タルン

2

これらのクラスに変更する理由は1つだけですか?

まだわからない場合は、投機的設計/オーバーエンジニアリングトラップ(クラスが多すぎる、複雑な設計パターンが適用されている)に陥っている可能性があります。あなたはYAGNIを満足していません。ソフトウェアライフサイクルの初期段階(一部)の要件は明確ではない場合があります。したがって、現在、変更の方向は表示されません。それに気付いたら、1つまたは最小限のクラスに保管してください。ただし、これには不利な点があります。要件と変更の方向が明確になるとすぐに、現在の設計を再考し、変更の理由を満たすためにリファクタリングを行う必要があります。

変更の3つの異なる理由があり、それらがこれらの3つのクラスにマップされることを既に知っている場合は、正しい用量のSRPを適用しました。繰り返しますが、これにはいくらかの責任が伴います。あなたが間違っていたり、将来の要件が予期せずに変更された場合、現在の設計を再考し、変更の理由を満たすためにリファクタリングを行う必要があります。

全体のポイントは次のとおりです。

  • 変更のドライバーに注意してください。
  • リファクタリング可能な柔軟なデザインを選択します。
  • コードをリファクタリングする準備をしてください。

この考え方をお持ちであれば、投機的設計/過剰エンジニアリングをあまり恐れることなくコードを作成できます。

ただし、時間には常に要因があります。多くの変更では、必要な時間がありません。リファクタリングには時間と労力がかかります。設計を変更しなければならないとわかっている状況に頻繁に遭遇しましたが、時間の制約のためにそれを延期しなければなりませんでした。これは発生し、回避することはできません。過剰に設計されたコードよりも、設計されたコードでリファクタリングする方が簡単であることがわかりました。YAGNIは良い指針を与えてくれます。疑わしい場合は、クラスの量とデザインパターンの適用を最小限に抑えてください。


1

クラスを分割する理由としてSRPを呼び出す理由は3つあります。

  • 将来の要件の変更により、一部のクラスが変更されないようにすることができます
  • バグをより迅速に分離できるように
  • クラスを小さくする

クラスの分割を拒否する理由は3つあります。

  • 将来の要件の変更によって、複数のクラスで同じ変更が行われないようにするため
  • バグ検出に間接的な長いパスの追跡が含まれないように
  • カプセル化を破る技術(プロパティ、友人など)に抵抗するために、関連する1つのクラスだけが必要とする情報やメソッドをすべての人に公開する

あなたのケースを見て、変更を想像すると、それらのいくつかは複数のクラスに変更を引き起こします(たとえば、元帳にフィールドを追加し、3つすべてを変更する必要があります)が、多くはそうではありません-たとえば元帳のリストにソートを追加または、新しい元帳エントリを追加するときに検証ルールを変更します。同僚の反応は、おそらくバグを探す場所を知るのが難しいためです。元帳のリストで何かが間違っているように見える場合、それは間違って追加されたためですか、それともリストに何か問題がありますか?要約と詳細に矛盾がある場合、どちらが間違っていますか?

また、3つのクラスすべてを変更する必要があることを意味する要件の変更は、1つだけを変更するだけで済ませる変更よりもはるかに高いと同僚が考える可能性もあります。それは本当に役に立つ会話になるかもしれません。


別の視点を見るのはいいことです
タルン

0

元帳がdbのテーブルである場合、これらのしきい値タスクを1つのクラス(DAOなど)に配置することをお勧めします。 (2つまたは3つのクラスである可能性があります)および顧客のために単純に行うためのファサードクラスを提供します。


元帳はUIの機能であり、それに関連するさまざまな操作があり、異なるページから実行できます。
タルン

0

私見では、クラスをまったく使用しないでください。ここにはデータの抽象化はありません。元帳は具象データ型です。それらを操作および表示する方法は、関数と手順です。日付の書式設定など、共有される一般的に使用される多くの関数は確かにあります。表示および選択ルーチンのクラスでデータを抽象化して非表示にする場所はほとんどありません。

元帳編集のためにクラスの状態を非表示にする場合があります。

SRPを悪用しているかどうかにかかわらず、より基本的なものを悪用しています。抽象化を使いすぎています。これは典型的な問題であり、OOは正気な開発パラダイムであるという神話的な概念によって生じます。

プログラミングは90%具体的です。データ抽象化の使用はまれであり、慎重に設計する必要があります。一方、機能の抽象化は、言語の詳細とアルゴリズムの選択を操作のセマンティクスから分離する必要があるため、より一般的です。


-1

必要なクラスは1つだけで、そのクラスの責任は元帳管理することだけです。


2
私にとって、「... manager」クラスは、通常、それをさらに分解することに煩わされないことを示しています。クラスは何を管理していますか?プレゼンテーション、永続性?
セバスチャンガイガー

-1。3つ以上のユースケースを1つのクラスに入れることは、SRPが回避しようとしていることであり、正当な理由があります。
Doc Brown

@sebastiangeigerでは、OPの3つのクラスは何をしているのでしょうか?プレゼンテーションまたは永続性?
sevenseacat

@Karpie私は正直にプレゼンテーションを期待しています。確かに、この例はあまり良くありませんが、Webサイトの '... Page'がビューに似た動作をしていることを期待しています。
セバスチャンガイガー

2
実際には、あなただけの単一責任を持って、アプリケーション全体のための1つのクラスを必要とするユーザーが幸せになっ ;-)
ペーテルTörök
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.