6

stackoverflow の「Open Closed Principle」については、多くの議論がなされてきました。ただし、一般的には、原則のよりリラックスした解釈が一般的であるように思われるため、たとえば Eclipse はプラグインを使用して変更できるようになっています。

厳密な OCP に従って、新しい動作を追加するためではなく、バグを修正するためだけに元のコードを変更する必要があります。

OCP を介して機能の進化を観察できる、パブリックまたは OS ライブラリで OCP の厳密な解釈の良い例はありますか?元のコードが変更されていない元のクラスが拡張された、ライブラリの次のバージョン。

編集: Robert C. Martin によると、「リンク可能なライブラリ、DLL、または Java .jar が変更されていないかどうかにかかわらず、モジュールのバイナリ実行可能バージョン」*。ライブラリが閉じられたままになっているのを見たことがありません。実際には、新しい動作がライブラリに追加され、新しいバージョンが公開されています。OCP によると、新しい動作は新しいバイナリ モジュールに属します。

*Robert C. Martin によるアジャイル ソフトウェア開発、原則、パターン、および実践

4

3 に答える 3

3

OCP の原則では、クラスは拡張に対してオープンであるが、変更に対してクローズされている必要があります。これを実現するための鍵は抽象化です。DIP 原則も読むと、抽象化は詳細に依存するべきではなく、詳細は抽象化に依存する必要があることがわかります。あなたの例では、インターフェイスに詳細があります (2 つの特定のメソッド bar() と foo2())。OCP を完全に実装するには、そのような詳細を回避する必要があります (たとえば、それらを抽象化の背後に移動し、代わりに異なる実装を持つ 1 つの一般的な foo メソッドを使用するようにしてください)。

たとえば、SolrNet のこのインターフェースを見て ください: https://github.com/mausch/SolrNet/blob/master/SolrNet/ISolrCommand.csそれ以上の詳細を与えないでください。

代わりに、詳細はインターフェースの実装にあります: https://github.com/mausch/SolrNet/tree/master/SolrNet/Commands

ご覧のとおり、他のクラスの実装を変更することなく、必要な数のコマンドを追加できます。これにより、特定の実装は変更に対して閉じられていると見なすことができますが、インターフェースにより、新しいコマンドで機能を拡張することができ、拡張のために開かれています。

(とにかく、SolrNet は異常ではありません。この投稿を読んだときにたまたまブラウザーにあったので、このプロジェクトの例を使用しただけです。ほとんどすべての優れたコード化された OO プロジェクトは、何らかの形で OCP 原則を利用しています)

編集: バイナリ レベルでこの例が必要な場合は、たとえば、独自の配送業者、支払いを追加できる nopCommerce (http://nopcommerce.codeplex.com/releases/view/69081) を参照してください。一連のインターフェイスを実装することにより、元の DLL に触れることなく、プロバイダーまたは為替レート プロバイダーを変更できます。繰り返しますが、これは nopCommerce にとって特別なことではありません。数日前に使用したので、頭に浮かんだ最初のプロジェクトでした ;)

OCP はバイナリ レベルでのみ使用される原則ではありませんが、適切な OOD はどこでもではなく、適切なすべてのレベルで OCP を使用します ;) バイナリ レベルでの「厳密な」OCP は常に適切であるとは限らず、あらゆる状況で使用すると、さらに複雑になりますが、実行時に実装を変更したい場合や、外部開発者がインターフェースを拡張できるようにしたい場合に最も興味深いものです。インターフェイスを設計するときは常に OCP の原則を念頭に置く必要がありますが、それを法律としてではなく、正しい状況で使用される原則と見なす必要があります。

Robert C Martin の言葉を引用するときは、Agile Principles, Patterns and Practices を参照していると思います。もしそうなら、同じ章で彼が上で述べたのと同じことについて述べている結論も読んでください。たとえば、彼の著書 Clean Code を読んだ場合、彼は OCP の原則についてより段階的な説明をしています。上記の引用は、常に新しいコードを新しい DLL:s、JAR に配置する必要があると人々に思わせる可能性があるため、少し残念だと思います。 :s または libs は、常にコンテキストを考慮する必要があるというのが真実です。

むしろ、OCP に関する Martins の最新のホワイトペーパーhttp://objectmentor.com/resources/articles/ocp.pdf (彼は後の著書 Clean Code でも参照しています) を参照する必要があると思います。バイナリを分離するのではなく、「クラス、モジュール、関数」と呼んでいます。これは、Martin が OCP について話すとき、バイナリ拡張だけでなく、クラスと関数の拡張も意味していることを証明していると思います。したがって、バイナリ拡張は、最初の例のクラス拡張よりも「厳密」ではありません。

于 2011-09-05T12:55:11.940 に答える
1

私は本当に良い例を知りませんが、もっと「リラックスした解釈」の理由があるかもしれないと思います(例えばここSOで):

現実のプロジェクトでOCPの原則を完全に実現するには、リーンインターフェイス(これについてはISPとDIPを参照)と依存性注入(プロパティまたはコンストラクターベースのいずれか)を介して結合を行う必要があります... 「リラックスした解釈」に訴える...

この点に関するいくつかの興味深いリンク:

于 2011-08-31T01:57:45.853 に答える
1

バックグラウンド

PPPの 100 ページで、Robert Martin は次のように述べています。

「変更のためクローズ」
モジュールの動作を拡張しても、モジュールのソース コードまたはバイナリ コードは変更されません。リンク可能なライブラリ、DLL、または Java .jar のいずれであっても、モジュールのバイナリ実行可能バージョンは変更されません。

また、103 ページでは、非 OCP 設計により既存のクラスが再コンパイルされる、C で記述された例について説明しています。

したがって、すべての witch/case ステートメントまたは if/else チェーンのソース コードを変更するだけでなく、Shape データ構造を使用するすべてのモジュールのバイナリ ファイルを (再コンパイルによって) 変更する必要があります。バイナリ ファイルを変更すると、DLL、共有ライブラリ、またはその他の種類のバイナリ コンポーネントを再デプロイする必要があります。

この本は 2003 年に出版され、例の多くは長いコンパイル時間で悪名高い言語である C++ を使用していることを覚えておくとよいでしょう (ヘッダー ファイルの依存関係が適切に処理されない限り - Remedy の開発者はあるプレゼンテーションでAlan Wake完全なビルドには約 2 分しかかかりません)。

したがって、小規模 (つまり、1 つのプロジェクト内) でのバイナリ互換性について議論する場合、OCP (および DIP) の利点の 1 つは、コンパイル時間が短縮されることです。これは、最新の言語やマシンではあまり問題になりません。しかし、大規模な場合、ライブラリが他の多くのプロジェクトで使用されている場合、特にそれらのコードが私たちの管理下にない場合、ソフトウェアの新しいバージョンをリリースする必要がないという利点が引き続き適用されます.

バイナリ互換性で OCP に従うオープン ソース ライブラリの例として、JUnit を見てください。JUnit の@RunWithアノテーションとRunnerインターフェースに依存する数十のテスト フレームワークがあるため、JUnit、Maven、IDE などを変更することなく、JUnit テスト ランナーで実行できます。

また、JUnit に最近追加された@Rule アノテーションにより、テスト ライターは、以前はカスタム テスト ランナーが必要だった標準 JUnit テストのカスタム動作にプラグインできます。もう一度、ライブラリ レベルの OCP の例を示します。

対照的に、TestNG は OCP には従いませんが、TestNG と JUnit のテストを異なる方法で実行するための JUnit 固有のチェックが含まれています。TestRunner.run ()メソッドから代表的な行を見つけることができます。

  if(test.isJUnit()) {
    privateRunJUnit(test);
  }
  else {
    privateRun(test);
  }

その結果、TestNG テスト ランナーがいくつかの面でより多くの機能 (たとえば、テストの並列実行をサポートするなど) を持っていても、TestNG を変更せずに他のテスト フレームワークをサポートするように拡張できないため、他のテスト フレームワークはそれを使用しません。(TestNG には-testrunfactory引数を使用してカスタム テスト ランナーをプラグインする方法がありますが、私の知る限りでは、スイートごとに 1 つのタイプのランナーしか許可されません。そのため、JUnit とは異なり、1 つのプロジェクトで多くの異なるテスト フレームワークを使用することはできません。)

結論

ただし、ほとんどの場合、OCP はアプリケーションまたはライブラリで使用されます。この場合、基本モジュールとその拡張機能の両方が同じバイナリ内にパッケージ化されます。そのような状況では、OCP はソース コードの保守性を向上させるために使用されますが、再デプロイや新しいリリースを避けるためではありません。変更されていないファイルを再コンパイルする必要がないという潜在的な利点はまだありますが、ほとんどの最新の言語ではコンパイル時間が非常に短いため、それはあまり重要ではありません。

常に心に留めておくべきことは、OCP に従うと、システムがより複雑になるため、費用がかかるということです。Robert Martin は、PPP の 105 ページと章の結論でこれについて語っています。OCP は、最も可能性の高い変更に対してのみ、慎重に適用する必要があります。OCP に従うために先制的にフックを配置するべきではありませんが、フックを必要とする変更が発生した後にのみフックを配置する必要があります。したがって、既存のクラスを変更せずにすべての新機能が追加されたプロジェクトを見つけることはほとんどありません-誰かがそれを学術的な演習として行わない限り(私の直感では、それは非常に難しく、結果として得られるコードはきれいではないと言います).

于 2011-09-05T22:20:41.377 に答える