単一責任原則は機能に適用できますか?


17

ロバートC.マーティンによると、SRPは次のよ​​うに述べています。

クラスが変更される理由は1つだけです。

ただし、彼の本Clean Code、第3章:関数では、次のコードブロックを示しています。

    public Money calculatePay(Employee e) throws InvalidEmployeeType {
        switch (e.type) {
            case COMMISSIONED:
                return calculateCommissionedPay(e);
            case HOURLY:
                return calculateHourlyPay(e);
            case SALARIED:
                return calculateSalariedPay(e);
            default:
                throw new InvalidEmployeeType(e.type);
        }
    }

そして次のように述べます:

この機能にはいくつかの問題があります。まず、それは大きく、新しい従業員タイプが追加されると成長します。第二に、非常に明確に複数のことを行います。第三に、変更する理由は複数あるため、単一責任原則(SRP)に違反しています。[強調鉱山]

まず、SRPはクラスに対して定義されていると思いましたが、関数にも適用できることがわかりました。第二に、この関数には複数の変更理由がありますか?従業員の変更が原因で変更が確認できます。


5
これは、ポリモーフィズムの教科書のようです。
-wchargin

これは非常に興味深いトピックです。この問題に次の解決策を追加する可能性はありますか?私は各従業員クラスにcalculatePay機能を配置することを断念しますが、これは悪い理由なので、各従業員クラスは1に変更できるためです。支払いの計算。など。クラスに複数のプロパティを追加する2
STAV ALFI

回答:


13

単一責任原則の詳細を見落とすことがよくあるのは、「変更の理由」がユースケースアクターによってグループ化されていることです(詳細な説明はこちらを参照してください)。

したがって、この例では、calculatePay新しいタイプの従業員が必要になるたびにメソッドを変更する必要があります。あるタイプの従業員は別のタイプの従業員とは何の関係もないかもしれないので、変更するとシステム内の異なるユーザーグループ(またはユースケースアクター)に影響するため、それらを一緒にすると原則に違反します。

ここで、原則が関数に適用されるかどうかについて:1つのメソッドでのみ違反があったとしても、複数の理由でクラスを変更しているため、依然としてSRP違反です。


1
リンクされたyoutubeビデオを視聴しようとしましたが、ユースケースの俳優によるグループ化については言及せずに10分後には、あきらめました。最初の6分間は、明確な理由はありませんが、すべてエントロピーについてとりとめがありません。あなたが彼がこれについて議論し始める場所をビデオで示したなら、それは役に立つでしょう。
マイケルショー

@MichaelShaw 10:40から視聴してみてください。ボブおじさんは、コードは「人によって異なる理由で変わる」と述べています。MichelHenrichが私たちを指し示しようとしているのはそれだと思う。
エンリケ

50分のyoutubeビデオ全体の視聴を終了しましたが、その大半は明らかにすべきことについてではありませんでした。16:00の時点で、彼はすでにそのトピックから移動していることに気付きました。「説明」は基本的にこれに要約されます。SRPの「責任」は、「変更のさまざまな理由」を意味するわけではなく、「異なる人々の要求に応じて変更」を意味します。人々が果たすさまざまな役割の要求」。「説明」は何も明確にせず、曖昧な単語を別の単語に置き換えます。
マイケルショー

2
@MichaelShawは本からの引用のように、異なる従業員タイプを紹介する必要がある場合は、Employeeクラスを変更する必要があります。さまざまな役割がさまざまな従業員タイプの支払いを担当する場合があるため、この場合、Employeeクラスを複数の役割に変更する必要があります。したがって、SRPに違反します。
ミシェルヘンリッヒ

1
@MichaelShawはい、そうです-SRPは組織の編成方法に依存します。それがまさに、すべてのコメントに「may」または「might」を追加する理由です:)。ただし、そのような場合でも、コードはSRPに違反していないかもしれませんが、確実にOCPに違反しています。
ミシェルヘンリッヒ

3

176ページ、第12章:出現、「最小限のクラスとメソッド」というタイトルのセクションで、この本は次のように説明することで多少の修正を提供しています。

クラスとメソッドを小さくするために、非常に多くの小さなクラスとメソッドを作成する場合があります。したがって、このルールは、関数とクラスの数も少なく保つことを示唆しています

そして

高いクラス数とメソッド数は、無意味な教義の結果である場合があります。

明らかに、彼は、calculatePay()上記のような完全に細かい無邪気な小さなメソッドを分解するために、SRPに従うドグマティズムについて話している。


3

マーティン氏がSRPを機能に適用すると、SRPの定義が暗黙的に拡張されます。SRPはOO固有の一般原則の文言であり、関数に適用する場合は良い考えなので、問題はありません(ただし、定義)。

変更する理由は複数ありません。また、「責任」または「変更する理由」の観点からSRPを考えることが役立つとは思いません。基本的に、SRPが目指しているのは、ソフトウェアエンティティ(関数、クラスなど)が1つのことを実行し、適切に実行する必要があるということです。

私の定義を見てみると、SRPの通常の表現よりも曖昧ではありません。SRPの通常の定義に伴う問題は、それらがあまりにも曖昧であるということではなく、本質的に曖昧な何かについてあまりにも具体的にしようとすることです。

あなたが何をするかを見るとcalculatePay、それは明らかに一つのことをしています:タイプに基づいたディスパッチ。Javaには型ベースのディスパッチを行う組み込みの方法があるため、calculatePay洗練されておらず、非イディオマティックです。したがって、記述された理由ではなく、書き換える必要があります。


-2

あなたは正しい@Enriqueです。それが関数であるかクラスのメソッドであるかに関係なく、SRPはそのコードブロックで1つのことだけを行うことを意味します。

「変更する理由」ステートメントは、少し誤解を招く場合がありますが、変更するcalculateSalariedPay場合、calculateHourlyPayまたはその列挙Employee.typeを変更する必要がある場合は、このメソッドを変更する必要があります。

あなたの例では、関数:

  • 従業員のタイプを確認します
  • タイプに応じてお金を計算する別の関数を呼び出します

私の意見では、それは直接SRP違反ではありません。なぜなら、従業員について考えてメソッドがすでに存在する場合、スイッチケースと呼び出しを短く書くことはできないからです。とにかく、従業員タイプを追加する場合は「case」ステートメントを追加する必要があるため、明確なオープンクローズド原則(OCP)違反であるため、実装が不適切です。リファクタリングします。

「お金」がどのように計算されるべきかはわかりませんが、最も簡単な方法はEmployeegetMoneyメソッドとしてインターフェイスといくつかの具体的な実装を用意することです。その場合、機能全体は不要です。

計算がより複雑な場合は、100%SRPではないビジターパターンを使用できますが、スイッチケースよりもOCPが多くなります。


2
関数が行う2つのことをどのようにリストできるかはわかりませんが、SRP違反ではないと言います。
ジェフ

@JeffO:それは2つのことではなく、1つのことの2つの部分です:型に基づいて適切な関数を呼び出す。
マイケルショー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.