web-dev-qa-db-ja.com

多くのソフトウェア開発者がオープン/クローズドの原則に違反しているのはなぜですか?

多くのソフトウェア開発者が、アップグレード後にアプリケーションを破壊する関数の名前を変更するなど、多くの変更を行うことによって open/closed主義 に違反しているのはなぜですか?

この質問は React ライブラリーの高速バージョンと継続バージョンの後で頭に飛びつきます。

短い期間ごとに、構文、コンポーネント名などの多くの変更に気付きました

Reactの次期バージョン の例:

新しい非推奨の警告

最大の変更は、React.PropTypesとReact.createClassを独自のパッケージに抽出したことです。どちらもメインReactオブジェクトからアクセスできますが、どちらかを使用すると、開発モードのときにコンソールに1回限りの非推奨警告が記録されます。これにより、将来のコードサイズの最適化が可能になります。

これらの警告は、アプリケーションの動作には影響しません。ただし、特にconsole.errorを失敗として扱うテストフレームワークを使用する場合は、これらがある程度のフラストレーションを引き起こす可能性があることを認識しています。


  • これらの変更はその原則の違反と見なされますか?
  • Reactのようなものの初心者として、ライブラリ内のこれらの高速な変更(どのようにイライラする)でそれをどのように学ぶことができますか?
77

IMHO JacquesBの回答は、多くの真実を含んでいますが、OCPの根本的な誤解を示しています。公平にするために、あなたの質問はすでにこの誤解も表現しています-関数の名前を変更すると後方互換性が壊れますが、OCPは壊れません。互換性を壊す必要があると思われる場合(または、互換性を壊さないように同じコンポーネントの2つのバージョンを維持する場合)、OCPは以前に既に壊れています!

JörgW Mittagがコメントですでに述べたように、原則は「コンポーネントの動作を変更することはできません」とは述べていません。つまり、コンポーネントを開いた状態でコンポーネントを設計する必要がありますtryいくつかの方法で再利用(または拡張)された場合、必要なし変更。これは、適切な「拡張ポイント」を提供することによって、または@AntPで言及されているように、「デフォルトですべての自然な拡張ポイントが存在するポイントにクラス/関数構造を分解することによって」行うことができます。 OCPに続く私見は「下位互換性のために古いバージョンを変更せずに保持する」と共通点はありません。または、以下の@DerekElkinのコメントを引用します。

OCPは、モジュールの変更方法に関するアドバイスであり、モジュールの変更を決して許可しない変更管理プロセスの実装に関するものではありません。

優れたプログラマーは、経験に基づいて、「正しい」拡張ポイントを念頭に置いてコンポーネントを設計します(または、さらに良いことに、人為的な拡張ポイントが不要な方法で)。ただし、これを正しく、不必要なオーバーエンジニアリングを行わずに行うには、コンポーネントの将来のユースケースがどのようになるかを事前に知る必要があります。経験豊富なプログラマーでさえ、将来を見据えて、すべての今後の要件を事前に知ることはできません。そして、それが時々後方互換性に違反する必要がある理由です-コンポーネントが持っている拡張ポイントの数、または特定のタイプの要件に関してOCPをどれだけ順守しているかに関係なく、常に要件がありますコンポーネントを変更せずに簡単に実装することはできません。

147
Doc Brown

オープン/クローズの原則には利点がありますが、いくつかの重大な欠点もあります。

理論的には原則は、「拡張のために開いているが変更のために閉じている」コードを作成することにより、下位互換性の問題を解決します。クラスに新しい要件がある場合は、クラス自体のソースコードを変更するのではなく、動作を変更するために必要な適切なメンバーのみをオーバーライドするサブクラスを作成します。したがって、クラスの元のバージョンに対して作成されたすべてのコードは影響を受けないため、変更によって既存のコードが破損していないことを確信できます。

実際にはコードの肥大化と時代遅れのクラスの混乱した混乱に簡単に終わります。拡張機能を使用してコンポーネントの一部の動作を変更できない場合は、必要な動作を備えたコンポーネントの新しいバリアントを提供し、下位互換性のために古いバージョンを変更せずに維持する必要があります。

たとえば、多くのクラスが継承する基本クラスの基本的な設計上の欠陥を発見したとします。エラーの原因は、プライベートフィールドのタイプが間違っていることです。メンバーを上書きしてこれを修正することはできません。基本的に、クラス全体をオーバーライドする必要があります。つまり、Objectを拡張して代替の基本クラスを提供することになります。また、すべてのサブクラスに代替を提供する必要があるため、オブジェクト階層が重複します。 1つの階層に欠陥があり、1つは改善されました。ただし、欠陥のある階層を削除することはできません(コードの削除は変更であるため)。将来のすべてのクライアントは両方の階層に公開されます。

この問題に対する理論的な答えは、「初めて正しく設計するだけ」です。コードが完全に分解され、欠陥やミスがなく、将来のすべての要件変更に備えて拡張ポイントが用意されている場合は、混乱を避けることができます。しかし、実際には誰もが間違いを犯し、誰も未来を完全に予測することはできません。

.NETフレームワークのようなものを取り上げてください。これは、ジェネリックが10年以上前に導入される前に設計された一連のコレクションクラスを保持しています。これは確かに下位互換性のための恩恵です(何も書き直さなくてもフレームワークをアップグレードできます)だけでなく、フレームワークが肥大化し、多くのオプションが時代遅れになっている多数のオプションが開発者に提示されます。

どうやらReactの開発者は、オープン/クローズドの原則に厳密に従うことは複雑さやコードの膨張にコストをかける価値がないと感じています。

Open/closedの実用的な代替手段は非推奨の制御です。単一のリリースで下位互換性を壊すのではなく、古いコンポーネントはリリースサイクルの間保持されますが、クライアントは古いアプローチが今後のリリースで削除されることをコンパイラの警告を介して通知されます。これにより、クライアントはコードを変更することができます。これはReactのアプローチのようです。

(私の原則の私の解釈は、ロバートC.マーティンによる The Open-Closed Principle に基づいています)

68
JacquesB

私は開閉の原則を理想と呼びます。すべての理想と同様に、ソフトウェア開発の現実についてはほとんど考慮されていません。また、すべての理想のように、実際にそれを実際に達成することは不可能です。その理想にできるだけ近づくように努力するだけです。

物語の反対側はゴールデン手錠として知られています。ゴールデン手錠は、開閉の原則に身を任せすぎると得られるものです。ゴールデン手錠とは、過去の過ちが多すぎて後方互換性を損なうことがない製品が成長できない場合に発生します。

この有名な例は、Windows 95のメモリマネージャーです。 Windows 95のマーケティングの一環として、すべてのWindows 3.1アプリケーションがWindows 95で動作することが述べられました。Microsoftは実際にWindows 95でそれらをテストするために数千のプログラムのライセンスを取得しました。問題のケースの1つはSim Cityでした。 Sim Cityには実際にはバグがあり、割り当てられていないメモリに書き込まれていました。 Windows 3.1では、「適切な」メモリマネージャーがないため、これはマイナーな偽物でした。ただし、Windows 95では、メモリマネージャーがこれをキャッチし、セグメンテーション違反を引き起こしていました。ソリューション? Windows 95では、アプリケーション名がsimcity.exe、OSは実際にメモリマネージャーの制約を緩和して、セグメンテーション違反を防ぎます!

この理想の背後にある本当の問題は、製品とサービスの単純な概念です。誰も実際にはどちらもしません。すべてが2つの間の灰色の領域のどこかに並んでいます。製品指向のアプローチから考えると、開閉は素晴らしい理想のように聞こえます。あなたの製品は信頼できます。ただし、サービスに関しては状況が変わります。古い機能をクリーンアップすることはできないため、オープン/クローズの原則では、チームがサポートする必要のある機能の量はmustに漸近的に近づく必要があることを示すのは簡単です。つまり、開発チームは毎年ますます多くのコードをサポートする必要があります。最終的には、限界点に到達します。

今日のほとんどのソフトウェア、特にオープンソースは、オープン/クローズの原則の一般的なリラックスバージョンに従っています。 open/closedがマイナーリリースでは従順であるが、メジャーリリースでは見捨てられるのはよくあることです。たとえば、Python 2.7には、Python 2.0および2.1日からの多くの「悪い選択」が含まれていますが、Python 3.0スイープ(また、Windows 2000をリリースしたときのWindows 95コードベースからWindows NTコードベースへの移行は、あらゆる種類の問題を解決しましたが、didはメモリを処理する必要がないことを意味しますマネージャーがアプリケーション名をチェックして動作を決定します!)

20
Cort Ammon

Doc Brownの回答は正確に最も近く、他の回答はオープンクローズ原則の誤解を示しています。

誤解を明確に説明するために、OCPは後方互換性のない変更(または any 変更またはこれらの行に沿ったもの)を行わないことを意味すると信じているようです。OCPは必要がないようにコンポーネントを設計することについてそれらの変更に下位互換性があるかどうかに関係なく、それらの機能を拡張するために変更を加える必要はありません。機能の追加以外にも、コンポーネントに変更を加えることができる理由は、下位互換性がある(リファクタリングや最適化など)か、下位互換性がない(機能の非推奨や削除など)か、他にもたくさんあります。これらの変更を行っても、コンポーネントがOCPに違反しているとは限りません(あなたがOCPに違反しているとは限りません)。

本当に、それはソースコードに関するものではありません。 OCPのより抽象的な関連性のあるステートメントは、次のとおりです。「コンポーネントは、その抽象化境界に違反する必要なく、拡張を許可する必要があります」。私はさらに進んで、より近代的な表現は「コンポーネントは enforce その抽象化境界を拡張する必要がありますが、拡張を許可する」と言います。ボブマーティンによるOCPに関する記事でさえ、「ソースコードは侵害されている」と「変更に近い」と「説明」しているが、その後、ソースコードの変更とは関係のないカプセル化と、抽象化に関係するすべてのことについて話し始めた。境界。

したがって、問題の誤った前提は、OCPがコードベースの進化に関するガイドライン(として意図されている)であるということです。 OCPは通常、「コンポーネントは拡張機能に対して開かれ、コンシューマによる変更に対して閉じられるべきである」とスローガン化されています。基本的に、コンポーネントの consumer がコンポーネントに add 機能を追加したい場合、古いコンポーネントを新しいコンポーネントに拡張できるはずです。追加機能を備えていますが、古いコンポーネントを change することはできません。

OCPは、コンポーネントの creator changeing または removing 機能については何も述べていません。 OCPは バグの互換性 を維持することを永遠に推奨していません。作成者は、コンポーネントを変更または削除することでOCPに違反していません。コンシューマがコンポーネントに機能を追加できる唯一の方法がコンポーネントを変更することである場合、あなた、またはあなたが書いたコンポーネントはOCPに違反しています。 monkey patching またはソースコードにアクセスして再コンパイルする。多くの場合、これらはどちらもコンシューマーにとってのオプションではありません。つまり、コンポーネントが「拡張用にオープン」になっていない場合、それらは運が悪いのです。彼らは単に彼らのニーズのためにあなたのコンポーネントを使うことができません。 OCPは、少なくとも特定の「拡張機能」のクラスに関して、ライブラリの利用者をこの立場に置かないように主張しています。ソースコードまたはソースコードのプライマリコピーに変更 /を加えることができる場合でも、多くの潜在的なネガティブがあるため、変更できないことを "偽装"するのが最善です。そうすることへの影響。

だからあなたの質問に答えるために:いいえ、これらはOCPの違反ではありません。 OCPは変更のプロポーザルではないため、作成者が変更を加えてもOCPに違反することはありません。ただし、変更はOCPの違反を作成する可能性があり、以前のバージョンのコードベースでのOCPの失敗が原因である可能性があります。 OCPは特定のコードのプロパティであり、コードベースの進化の歴史ではありません。

対照的に、下位互換性はコードの change のプロパティです。一部のコードに下位互換性がある、または互換性がないと言っても意味がありません。一部のコードの下位互換性 /に関するいくつかの古いコードとの互換性について話すことだけが理にかなっています。したがって、一部のコードの最初のカットについて、下位互換性があるかどうかについて話しても意味がありません。コードの最初のカットは、OCPを満たすか、または失敗する可能性があります。一般的に、コードの履歴バージョンを参照せずに、一部のコードがOCPを満たすかどうかを判断できます。

あなたの最後の質問に関しては、それは主に意見に基づいているため、一般にStackExchangeのトピックから外れていると思われますが、ここ数年の間にあなたが説明した現象が呼び出されてきた技術、特にJavaScriptは歓迎されます JavaScript疲労 。 (自由に google にアクセスして、他のさまざまな記事を見つけてください。いくつかの風刺的な記事は、これについて複数の観点から話しています。)

11