web-dev-qa-db-ja.com

相続と単一責任原則の関係

例1:

基本クラスAがあると仮定します。クラスBはクラスAを拡張します(クラスBは派生クラスです)。

結論として、クラスBは継承の概念を使用しているため、単一責任の原則に従いませんか?つまり、クラスAに特定の変更が加えられると、クラスBにも変更が必要になりますか?したがって、これらの変更の責任はClass AとClass Bの間で共有されますか?

例2:

class Monitorable_stack extends Stack {
    private int high_water_mark = 0;
    private int current_size;
    public void Push( Object article )
    { if( ++current_size > high_water_mark )
        high_water_mark = current_size; super.Push(article);
    }
    public Object pop()
    {
        --current_size;
        return super.pop();
    }
    public int maximum_size_so_far()
    {
        return high_water_mark;
    }
}

SOLID原則はこのコードに準拠しており、SOLID原則をよりよく理解するためにいくつかの団体が私を助けることができますか?

3

ボブおじさんの単一責任原則に関する記事 原則の発明者は次のように説明しています:

SRPは、各ソフトウェアモジュールに変更する理由が1つだけあることを意味します。

しかし、彼の説明では、「理由」は非常にあいまいであり、客観的な技術要素よりも意思決定に関連していることをすぐに理解できます。

この原則は人に関するものです。

したがって、原則の全体的な表現は完全に誤解を招くものです。幸いにも、彼は別の言い回しを提供します:

同じ理由で変化するものをまとめますさまざまな理由で変化するものを分離します。

そしてここですべてが明らかになります:Aに一般的なAの概念に関連するすべて(たとえば、VehicleStack)、およびBに関連するすべてを集めますAの特殊化(例:Car、a MonitoredStack)。責任を混在させないでください(たとえば、Stackでの監視に関連しない一般的なMonitoredStackの動作を変更しないでください。Stack基本クラスで監視の問題を引き起こさないでください)。

したがって、継承は、適切に行われた場合、SRPに違反しません。 Bの変更でAの変更が必要な場合にのみ、SRPに違反します。

最後に、継承がSRPに反する場合、SOLIDは存在しません。継承が[〜#〜] o [〜#〜](開く/閉じる)そして、[〜#〜] s [〜#〜]と矛盾します。

6
Christophe

不必要な密結合を簡単に作成できるため、継承は実際に問題になる可能性があります。それらは必ずしも悪いわけではありませんが、同じことが作曲で達成できるかどうか常に検討する必要があります。

たとえば、あなたの例では、責任は実際にはかなりよく分離されているように見えます。 Monitorable_stackの実装は、スタックの内部には結合されていません。ただし、これは、最初から継承を使用する必要がないことも意味します。 Monitorable_stackは、stackをラップして同じインターフェースを提供するが、stackから継承しないアダプターである可能性があります。これにより、内部への結合のリスクを回避できますが、定型コードが増える代わりに、パブリックインターフェイスのすべてのメソッドを基になるstackに委任する必要がありますが、継承では必要なものだけをオーバーライドできます変更する。

stackに非公開のcurrent_sizeがあり、それを保持する代わりにmonitorable_stackが観察した場合、カップリングのより悪い形式になります。その後、stackの実装がこのカウンタを使用しないように変更された場合、monitorable_stackは(おそらく他のサブクラスも)壊れます。 SOLIDには、このような問題を回避するためのオープン/クローズドの原則がありますが、最初からカップリングを回避することをお勧めします。

単一責任の原則は紛らわしい名前が付けられ、誤解されることが多いため、懸念の結合と分離について話したいと思います。

1
JacquesB

数年間、単一責任の原則を適用しようとするのを待ちます。学習者として、あなたは必然的にそれを完全に間違ったものにするでしょう。

継承を使用するなど、かなり合理的なことをしたいが、SRPを理由にすべきではないと思うときはいつでも、SRPを間違って理解している可能性が高いです。

この場合、スタックの実装を変更してもサブクラスには影響しません。 Stackのインターフェースを変更するとeveryoneに影響するため、サブクラスは他のコードと同じ状況になります。 SRPとは関係ありません。

1
gnasher729

結論として言えば、クラスBは継承の概念を使用しているため、単一責任の原則に従いません。

いいえ。SRPは、単一の責任を持つ各クラスに焦点を合わせています。 FooRepositoryFooオブジェクトを取得する方法を知っていますが、たとえば探しているオブジェクトが見つからない場合にメッセージを記録する方法の具体的な実装の詳細を知っている。その二次的な責任は、実際には別のクラスの(唯一の)責任でなければなりません。 Logger。すべてのクラスはone事を行う方法を知っています、そしてそれがSRPが規定するものです。クラスには1つの責任があり、複数のことを実行する必要がある場合は、他のクラスへの依存関係(それぞれが1つの責任を持つ)があります。

SRPは、反転した、つまり2つのクラスが同様の(ただし、何らかの点で異なる)責任を負うことに重点を置きません。あなたが考えているのはDRY(Do n't Repeat Yourself))で、不要なコードの繰り返しに焦点を当てています。
これら2つの類似しているが異なるクラスは通常、DRYに違反しますが、SRPに違反しません。そして、それは無視)これらの2つのクラスには、共通の祖先があるかどうかがあります。

つまり、クラスAに特定の変更が発生すると、クラスBにも変更が必要になりますか?

これは、継承が機能するための方法ではありません。クリーンなコードベースでは、Bへの変更を必要とするAへの唯一の変更は、Aのcontractへの変更です。そのような変更の結果(つまり、Bを変更する必要がある)は避けられません。 SRPに関係なく。

Aの非契約変更によりBを変更する必要がある場合は、継承を適切に(または完全に)実装していません。継承された階層でも、各クラスには独自の責任があります。これらは機能レベルで密接に関連していますが、実際には重複していません。継承の全体的なポイントは、(そうでなければ結合された)クラスの責任を2つの個別チャンクに分割することです。

したがって、これらの変更の責任はクラスAとクラスBの間で共有されますか?

「変更に対する責任」は概念ではなく、SRPに関するものではありません。

パブリック契約を変更すると、常に(おそらく)消費者によるこの契約の使用法を変更する必要があります。これは、契約が正確に何であるかを定義すると、論理的で避けられない事実です。

SOLID原則はこのコードに準拠しており、SOLID原則をよりよく理解するためにいくつかの団体が私を助けることができますか?

これは、スニペットに基づいて実際に答えることはできません。

  • (S)責任が明確に記述されておらず、状況が明確でない。単一の責任とは何か、責任の融合とは何であるかを見分けることができません。
  • (O)ベースではなく、派生クラスのみを見て判断することはできません。継承の意図はOCPに準拠しているようですが、それは単なる野生の推論です。
  • (L)表示されたコードではポリモーフィズムを使用していません。基本クラスに対する消費者の期待が何であるか、およびそれがどのように消費されることが意図されているかが不明です。
  • (I)示されたコードで使用されているインターフェースはありません
  • (D)表示されたコードで使用されている依存関係はありません
0
Flater