web-dev-qa-db-ja.com

クラスの継承(C#で)は、オープン/クローズの原則に違反しますか?

私は通常、すべてのメソッドをprivateにします。ただし、サブクラスでオーバーライドする必要がない限り、そうするまでは。その時点で、基本クラスを変更して、特定のメソッドを作成しますprotected virtual。これは「変更のクローズ」の原則に違反していると言っても間違いありませんか?もしそうなら、それは私がprotected virtualデフォルトとして(Javaに似ており、メソッドはデフォルトで仮想です)。それは本当にOCPが推奨することですか?

個人的には、そのような解釈は私には少々手間がかかるようですが、何かが足りないかどうか知りたいです。

2
kmote

まず、最初の開発(ここでは基本クラスの開発)と後の開発段階の間には根本的な違いがあると思います。

最初の開発カテゴリにいる場合、基本クラスを最初のサブクラスに対応させるためにいくつかのリファクタリングが必要になることがよくあります(場合によっては、パラメーターの追加、メソッドの分割など)。したがって、リファクタリングの必要性に関しては、このような早い段階でOCPについて心配する必要があります。

さらに、私たちは何かの可能なすべての潜在的な将来の使用を予測することはできませんし、すべきではありません。それを行うこと自体がアンチパターンです。

また、内部クラスと外部公開クラスの間には根本的な違いがあります。内部クラスの場合、それらはすべて一緒にバージョン管理される場合があります(たとえば、同じDLL内)。そのため、設計の進展に応じて、新しいサブクラスに対応するためのリファクタリングを禁止する理由はありません。

ただし、既に実装されている多数のサブクラス(またはサードパーティのプラグイン)の成熟度が外部に公開されているAPI(異なるバージョン管理ユニット(異なるDLLなど)に追加することも期待できる)の場合、OCPを真剣に検討する必要があります:より良いデザインは、変更なしでより多くのサードパーティサブクラスに対応します。

ただし、外部に公開されているAPIについては、とにかくサブクラス化するのではなく、インターフェイスを使用する方がよいでしょう。サブクラス化は、インターフェイスとの比較によって実装者のオプションを大幅に制限すると同時に、プロデューサーとコンシューマー間の相互作用と依存関係を複雑にします。インターフェイスを使用して、メソッドを仮想としてマークするかどうかの問題を単に回避します。インターフェースを選択すると、設計がより分離され、OCPの精神がより強くなります。

6
Erik Eidt

サブクラスでオーバーライドする必要がない限り、通常はすべてのメソッドをプライベートにします。その時点で、基本クラスを変更して、その特定のメソッドを仮想保護にします

それはめったに良い戦術ではありません。最初にクラスを作成するときは、すべてのメソッドを公開しないようにする必要がありますprivate。フルストップ。これらのメソッドは後でprotectedになりません-メソッドは通常、後でオーバーライドされます適切に設計されていませんオーバーライドされます。メソッドを作成したときに、それらの意図された目的が拡張ポイントではなかったためです。それらすべてを作るprotected virtualデフォルトではそれは変更されません-技術的にメソッドをオーバーライドできるというだけでは、これが意味をなさないという意味ではありません。独自にインスタンス化することを目的としたクラスは、他のクラスの基本クラスになるのに適した候補となることはめったになく、プライベートメソッドを「保護された」に切り替えようとすることは、OCPに違反するだけでなく、おそらく悪い結果につながります。または継承の誤った使用法。

一方、abstractクラスAを「トップダウン」方式で設計し、それがクラスBの基本クラスになることがわかっている場合、 CおよびD、AはB、C、およびDのある種の一般化であるため、通常、最初から、AのどのメソッドをB、C、およびDでオーバーライドするために保護する必要があるかがわかります。 public最初から正しい、それらはオーバーライドされるように設計されています。

このような設計を「ボトムアップ」で作成することもできます。クラスBから始めて、次に2番目のクラスCを作成します。両方の共通点を確認し、共通メソッドを共通基本クラスまたは共通インターフェイス A(CをBの派生物にしようとするのではなく、OCPを誤って追跡しようとします)。これで、BとCで上書きする必要があるAのメソッドはすべて、最初からすべてパブリックまたは保護されています。もちろん、BとCは変更する必要がありますが、BとC 拡張されたクラスではありません-OCPを要求しても意味がありません。ただし、理想的には、後で別のクラスDがAから継承するときにAを変更する必要はありません。その場合は、AがOCPに確認します。

つまり、OCPに続くクラスは、メソッドを「保護」するだけで既存のクラスから作成されるのではなく、「拡張ポイント」を意図的に選択して、その目的のために設計する必要があります。

3
Doc Brown

継承は、OCPを実行するためのOKメカニズムです。ただし、OCP +継承はLSPを壊す可能性があることを指摘しておきます。それは、継承が直交する動作/責任を十分にサポートしていないためです。

ただし、プレーンな古いコンポジションはLSPを満足させながらOCPを実行することもできます;)そのような例の1つは戦略パターンです。もう1つは、パラメトリックポリモーフィズム(別名ジェネリック)です。

0
przemo_li