web-dev-qa-db-ja.com

オブジェクトがそれ自体を変更する方法を知っている場合、それは単一責任原則に違反しますか?

私は、自身の状態を変更する方法を知っているPlayerオブジェクトのような例を扱ってきました。

別の例は、アルゴリズムを使用して請求額を計算する方法を知っているInvoiceオブジェクトです。

これは単一責任の原則に違反していますか?

はいの場合、InvoiceCalculatorを作成することは、アルゴリズムを収容するための適切なアプローチですか?

そして、PlayerStateChangerは有効なものです。

8
GaVaRaVa

「単一の責任の原則」は、その名前が混乱しているため、善よりも害を及ぼしているという結論に達しました。

原則はnotは、クラスが特定のことを1つだけ行うことを許可されるべきであると言います。それが事実なら、それは本当に愚かな原則でしょう!

その状態を変更する方法を知っているオブジェクトは完全に問題ありません-実際、多くの人はonlyオブジェクト自体がその状態を変更できるはずであると主張します。請求書料金を計算できる請求書オブジェクトもまったく問題なく聞こえます。

SRPは、独立して変化する可能性のある懸念を分離することに関するものです。自身をPDFandとしてレンダリングするためのロジックを含む請求書オブジェクトは、料金を計算するための原則を破るでしょう。請求書の外観とレイアウトは、料金計算のアルゴリズムとは関係なく変更される場合があります。

(ここでの「変更」とは、別の問題であるランタイム状態の変更ではなく、開発者にコードの変更を強制する要件の変更を指すことに注意してください。)

SRPは、分離を確実にするために、多数の小さなクラスを定義する道を進んでいく可能性があります。しかし、原則は対応するものとバランスを取る必要があります。つまり、doが一斉に変化するものが必要なものコードで結合されている。これは「低結合であるが凝集度が高い」と呼ばれます。

20
JacquesB

自分自身を変更できるデータオブジェクト(ドメインオブジェクト)を設計しないことをお勧めします。

私は次の区別をすることを好みます:

  1. ドメインオブジェクト-データを運ぶオブジェクト。 PlayerInvoice。できれば不変ですが、アプリに依存します(たとえば、ゲームの場合、効率を上げるために可変オブジェクトが必要になる場合があります)
  2. ビジネスロジックを含むオブジェクト:InvoiceCalculatorまたはBanksに預金するAccountオブジェクト

利点:

  • ドメインオブジェクトは不変であるため、競合状態やスレッド同期の必要性を心配することなく、異なるスレッドに渡すことができます。
  • ドメインオブジェクトをより簡単にシリアル化できます
  • それらをキーとしてセットおよびマップに配置できます(equals/hashCodeを実装する場合)。それらは不変なので、安全に実行できます。
  • ビジネスクラスで同様のロジックを持つことにより、口座に出金/入金する銀行クラス:
    • 探しているときにこの機能を簡単に見つけることができます
    • さまざまなスレッドからのアクセスをより適切に制御できます

上記で説明したのは、Martin Fowlerによって 貧血ドメインモデル として特徴付けられています。私はそれを懸念の分離と呼びます。

プログラミングはドグマではないので、「本当のOOPではない」と言われる可能性があることに落胆しないでください。 OOPは1つのパラダイムにすぎません-手続き型および機能型のものもあります。

2003年に記事が書かれたとき、マルチコアプログラムは今日ほど一般的ではありませんでした。現在はそうなっているので、データオブジェクトをシンプルで不変に保ち、他の場所でロジックを処理したいと思っています。

3
Minas Mina

ソフトウェアには多くの利害関係者がいて、それらの利害関係者のそれぞれがソフトウェアの変更を要求する場合があります。

たとえば、請求アプリケーションには複数の利害関係者がいます。

  • 請求書データの保存方法を担当するデータベース管理者
  • 各アイテムの課税方法と割引の計算方法に関する法的要件を提供する最高業務責任者(COO)
  • 印刷される請求書レポートでデータをどのようにレイアウトするかを決定する会計士

単一責任原則の目的は、利害関係者Aから要求された変更を行うときに、利害関係者Bの何かにブレーキをかけるのを助けることです。単一責任は、各クラスが会社の1人(またはそれ以上)の役割のみに責任を負う必要があることを意味します。

あなたがやりたい最後のことは、別のdbスキーマに移行している間、または会計士があなたに求めたレポートのレイアウトを変更している間に、税の計算方法を壊すためにCOOを怒鳴らせることです。

ここで、特にこの例の請求書ケースの場合、BCEモデルでは、アプリケーションに依存しないロジックとアプリケーション固有のロジックを区別することをお勧めします。請求書は、ビジネス(別名ドメイン)オブジェクトです。このオブジェクトは、請求書を処理する必要がある社内の他のアプリケーションでも使用できます。 addItem()、removeItem()などのメソッドは、ユースケースにとらわれないため、ビジネスオブジェクトである請求書クラスに属します。現在、特定のユースケースに関するコードは、コントローラー(またはインタラクター)と呼ばれる別のクラスに属しています。料金の計算は、請求のユースケースの一部であり、おそらく別々に置くと思います。これらのルールはその1つのアプリケーションまたはユースケースに固有のものではないと主張することもできるため、これは主観的です。

1
Lefteris E