-2

OCPに関するウィキペディアの記事は次のように述べています(私の強調):

...オープン/クローズドの原則では、「ソフトウェアエンティティ(クラス、モジュール、関数など)は拡張のために開いている必要がありますが、変更のために閉じている必要があります」と述べています...これは、ソースコードが変更さ れる実稼働環境で特に役立ちます。製品での使用を認定するために、コードレビュー、単体テスト、およびその他のそのような手順が必要になる場合があります。原則に従ったコードは、拡張しても変更されないため、そのような作業は必要ありません

それで、自動化された単体テストがない場合、OCPは価値があると正しく読んでいますが、必ずしもある場合そうではありませんか?それともウィキペディアの記事は間違っていますか?

4

8 に答える 8

5

ユニットテストは、定義上、ユニット(通常は 1 つのクラス)の動作に関するものです。テストを適切に実行するには、テスト対象のユニットを他のユニットとの相互作用から分離するために最善を尽くします (たとえば、モッキング、依存関係の挿入、およびすぐ)。

OCP は、ユニット (「ソフトウェア エンティティ」)の動作に関するものです。エンティティ A がエンティティ B を使用する場合、それを拡張することはできますが、変更することはできません。(ウィキペディアの記事がソース コードの変更だけに重点を置いているのは誤りだと思います。この問題は、ソース コードの変更によって取得されたものであろうと、他のランタイム手段によって取得されたものであろうと、すべての変更に適用されます)。

A が B を使用する過程で B を変更した場合、B も使用する無関係なエンティティ C が後で悪影響を受ける可能性があります。この場合、通常、適切な単体テストでは破損を検出できません。これは、単体に限定されていないためです。これは、A が B を使用し、次にC も B を使用しようとするという、微妙で特定の一連のユニット間の相互作用に依存します。統合、回帰または受け入れテストがそれをキャッチするかもしれませんが、実行可能なコードパスを完全にカバーするようなテストに頼ることはできません (単体テストであっても、1 つのユニット/エンティティ内で完全にカバーすることは困難です!-)。

いくつかの点で、これの最も明確な実例は、動的言語で許可され、そのような言語の実践者の一部のコミュニティで人気のあるモンキーパッチの論争の的となっている実践にあると思います (すべてではありません!-)。モンキー パッチ (MP) は、ソース コードを変更せずに実行時にオブジェクトの動作を変更することを目的としているため、ソース コードの変更だけではOCP を説明できないと思います。

MP は、今挙げた事例をよく表しています。A と C の単体テストは、各ユニット自体が正常に動作するため、(両方ともモックではなく実際のクラス B を使用している場合でも) 見事に合格できます。両方をテストしても (すでに UNIT テストをはるかに超えています)、たまたま A の前に C をテストしても、すべて問題ないように見えます。しかし、たとえば、A がメソッド B.foo を設定して B にサルパッチを適用し、45 (B が文書で提供し、C が依存しているため) ではなく 23 (A の必要に応じて) を返すとします。これは OCP を壊します: B は変更のために閉じられるべきですが、A はその条件を尊重せず、言語はそれを強制しません。次に、A が B を使用 (および変更) し、C の番になると、C はテストされたことのない状態で実行されます。すべてのテストで常に45 を返しました...!-)。

MP を OCP 違反の標準的な例として使用することの唯一の問題は、MP をあからさまに許可しない言語のユーザーの間で誤った安心感を生み出す可能性があることです。実際、構成ファイルとオプション、データベース (すべての SQL 実装が許可する場所ALTER TABLEなど;-)、リモーティングなどを通じて、十分に大きく複雑なプロジェクトはすべて、たとえそれが Eiffel で書かれていたとしても、OCP 違反に注意を払わなければなりません。またはHaskell(そして、CやC ++のように、適切なキャスト呪文が配置されている限り、「静的」とされる言語が実際にプログラマーがメモリのどこにいても好きなものを突き刺すことができる場合はなおさらです-今ではそれがそのようなものですあなたは間違いなくコードレビューでキャッチしたいです;-)。

「修正のため閉鎖」は設計上の目標です。バグが見つかった場合、バグを修正するためにエンティティのソース コードを修正できないという意味ではありません (その後、コード レビュー、回帰テストを含むさらなるテストが必要になります)。もちろん、バグは修正されています)。

IWhateverEx「リリース後に変更不可」が広く適用されているのを私が見た 1 つのニッチはIWhatever2、Microsoft の古き良き COM などのコンポーネント モデルのインターフェイスですIWhateverEx2。インターフェースへの修正が必要であることが判明した場合 --オリジナルに決して変更しないでIWhateverください!-)。

それでも、保証された不変性はインターフェイスにのみ適用されます。これらのインターフェイスの背後にある実装には、バグ修正、パフォーマンス最適化の調整などを含めることが常に許可されています (「最初から正しく実行する」ことは、SW 開発では機能しません。 : 100% 確実にバグがなく、使用されるすべてのプラットフォームで可能な最大の必要なパフォーマンスが得られる場合にのみソフトウェアをリリースできる場合、何もリリースすることはなく、競合他社があなたのランチを食べてしまいます。 d 倒産します;-)。繰り返しになりますが、このようなバグ修正と最適化には、通常どおりコード レビューやテストなどが必要になります。

あなたのチームでの議論は、バグ修正 (それらを禁止することについて議論している人はいますか?-) やパフォーマンスの最適化からではなく、むしろ新しい機能をどこに配置するかという問題から来ていると思います -foo既存のクラスAに新しいメソッドを追加する必要がありますむしろ、 「変更のために閉じられた」ままになるように、拡張ABて追加fooするBだけですか?A単体テスト自体は、まだこの質問に答えていません。既存のすべての使用法を実行できない可能性があるためですA(Aエンティティがテストされるときに、別のエンティティを分離するためにモックアウトされる可能性があります...)。したがって、1 つのレイヤーに進む必要があります。より深く、foo正確に何が行われているか、または行われている可能性があるかを確認します。

fooが単なるアクセサーであり、それが呼び出されたインスタンスを変更しない場合、Aそれを追加することは明らかに安全です。fooインスタンスの状態とその後の動作を他の既存のメソッドから観察できるように変更できる場合は、問題があります。OCP を尊重fooし、別のサブクラスを配置すると、変更は非常に安全で日常的になります。に簡単に配置したい場合は、広範fooコードレビュー、軽い「ペアワイズコンポーネント統合」テスト、すべての使用を調査する必要がありますAAなど。これはアーキテクチャの決定を制約するものではありませんが、どちらの選択にも異なるコストがかかることを明確に示しているため、適切に計画、見積もり、優先順位を付けることができます。

マイヤーの口述と原則は聖典ではありませんが、適切に批判的な態度をとれば、特定の具体的な状況に照らして研究し、熟考する価値があります。

于 2009-09-13T16:15:29.393 に答える
3

あなたはいまいましいOCPを読みすぎていると思います。私の解釈は、「あなたによって制御されていない多くのコードに依存する振る舞いを持つ既存のクラスを変更する前に、よく考えてください」です。

ユーザーがあなたとあなたの犬だけの場合、もちろん、非常に効率的でまったく問題なく、クラスから内臓を変更できます。

ユーザーが (内部または外部に関係なく) 多数の場合、労働者階級内の変更が及ぼす可能性のあるすべての影響を実際に考慮する必要があります。ユーザー ベースが巨大な場合は、予測することはできず、に:

  • 誰かのために何かを壊すリスク
  • 彼らにそれを延長させてください
  • 自分で拡張してデザインを膨らませる

ユースケースに最適なものを選択してください。

いつものように、コンテキストとトレードオフを理解することは、エンジニアリングを面白くするものです。適切なツールをいつ選択するかを知ってください。OCP が適用されない場合もありますが、A と B のコンテキストに適用されないために OCP を検討して拒否した場合、その有効性は検証されません。

于 2009-09-13T15:03:22.610 に答える
1

優れた設計原則(OCPなど)は、優れた開発プロセス(単体テストやTDDなど)とのオッズを追加するものではありません。それらは補完的です。

ウィキペディアの記事IMOは、OCPを使用している場合でも、単体テストやコードレビュー(XPではこれはTDDとペアプログラミングに変換されます)などの高品質のプロセスを常に使用していることを前提としています。さらに言えば、OCPを使用すると、変更の範囲をより適切に制御できるため、これらの品質プロセスの労力が軽減されます。

于 2010-10-08T16:01:41.627 に答える
-1

自動化された単体テストを使用している場合でも、それは依然として価値のある原則だと思います。

于 2009-09-13T14:52:06.223 に答える
-1

コードの一部に変更を加えると、たとえばメソッドの名前を変更するなどして、テストが失敗する可能性もあります。これがOCPの目的です。以前のコードを編集して動作を変える必要がある場所にコードを作成しないでください。代わりに、別の動作が必要な場合は、拡張機能を作成して実行できるようにします。

この種のデザインは多くの場所で見ることができます:(N)Hibernate Interceptors、WPFデータテンプレートなど。:)

于 2009-09-13T14:55:38.523 に答える
-1

C2 Wiki のこの記事では、OCP と XP の間の緊張関係について説明しています。

その記事のいくつかのコメントと、この学術論文(セクション B.2) から、「単体テストでは OCP を適用する必要がありますか?」という質問に対する答えが得られます。no のように見えます。

その理由は、OCP は、機能変更が機能するコードに与える影響を事前の設計を通じて対処するためです。抽象化は、後で新しい要件が発生したときに十分な拡張性を提供することを期待して、早い段階で作成されます。

一方、アジャイル開発 (XP または単に TDD を使用) は、前もって抽象化を考案しようとするのではなく、進化的設計 (「変更を受け入れる」という人はいますか?) を通じて変更の影響に対処します。誰もが知っているように、事前の設計は実際にはほとんど機能しません。

于 2009-09-13T21:01:12.833 に答える
-2

バートランド・メイヤーが考案したほとんどのアイデアと同様に、オープン/クローズド原則はほとんど完全に間違っています。システムにいくつかの新しい機能が必要であり、その機能が既存のクラスに属している場合は、そのクラスを変更します。恣意的な法則を満たすためだけに他の場所に置いてはいけません。

于 2009-09-13T14:53:44.817 に答える
-2

単体テストは、オープン/クローズの原則を維持するのに役立ちます。必要な変更(不正なコードのリファクタリング)は、外部から見える動作の変更が発生していないことを確認するために、単体テストスイートによって検証されます。

于 2009-09-13T14:57:34.280 に答える