オープン/クローズド原則を明確にする


25

私がそれを説明したように、オープン/クローズされた原則は、一度書かれたコードは修正されるべきではないと述べています(バグ修正を除く)。しかし、ビジネスルールが変更された場合、それらの変更を実装するコードを変更すべきではありませんか?私はそれが私には意味をなさないので、私は原理がどのようにかについて何かを理解していないと思う。

回答:


22

これはおそらく、説明するのに最も堅実な原則です。私が試してみましょう。完璧に機能し、バグのないInvoiceクラスを書いたと想像してください。請求書のPDFを作成します。

次に、誰かが、リンクを含むHTML請求書が欲しいと言います。この要求を満たすために請求書のコードを変更することはありません。代わりに、別のクラス、HTMLInvoiceを作成します。これは、現在必要なことを行います。HTMLInvoiceで多くの重複コードを記述する必要がないように、継承を利用します。

古い請求書を使用していた古いコードが破損したり、実際に影響を受けたりすることはありません。新しいコードはHTMLInvoiceを使用できます。(LのLiskov Substitutabilityも行うと、Invoiceインスタンスを想定している既存のコードにHTMLInvoiceインスタンスを与えることができます。)誰もが幸せに暮らしています。

請求書の変更は終了し、延長は可能です。また、これを機能させるには、事前に請求書を適切に作成する必要があります。


1
ビジネスルールが変更された場合、バグなしで完全に動作するという前提がないため、オープン/クローズの原則は適用されませんか?
ジェフ

私は自分でこのルールに苦労しました、そして、ケイトが示唆するものは基本的に私がそれについて結論づけたものです。ビジネスでは、より小さく、より柔軟なクラスをプログラムして、再利用できるようにします。それらが正常に機能している場合、それらを変更する必要はありません。しかし、それらが完全に「完了」することはめったにないため、一部の変更は避けられません。ただし、テキストでは「モジュール」と表示され、オブジェクトではありません。機能レベルでOCPをうまく適用することがよくあります。1つのことを完璧に行い、変更する必要のないタイトな機能を使用します。
CodexArcanum

1
@Jeff OIは、バグの修正(コードが元の要件を満たさず、誰もそれを望んでいない場合)と要件の変更を区別します。私はPDFを必要とし、コードはPDFを作る場合、私は今、HTMLたいにもかかわらず、何の不具合もありません(通常は、人々はHTMLだけでなく、ない代わりにしたい。)
ケイト・グレゴリー

2
@Winston-これは、Invoiceを適切に作成する必要があると言ったときの意味です。理想的には、すでにかなり抽象的な請求書があり、これを期待してPDFInvoiceを継承しました。そうでない場合は、将来ルールを破らないように設定するために、ルールを一度破る必要があります。いずれにせよ、将来の変化を予測することはこのすべての大きな部分です-そして、それはレシピの「象を捕まえて切り取る」部分です。
ケイトグレゴリー

1
あなたの最後の声明が最も重要です。オープン/クローズは理想的な設計です。そして、それを実現するためには設計を前もって取得する必要があります。すべてがオープン/クローズを満たしている必要もありませんが、そこに到達できれば強力なツールです。
アレックスファインマン

13

ObjectMentorのボブおじさんの仲間によるオープンクローズドプリンシパルの記事を読みましたか?私はそれがそこにあるより良い説明の一つだと思います。

オブジェクト指向設計には多くのヒューリスティックが関連付けられています。たとえば、「すべてのメンバー変数はプライベートにする必要があります」、「グローバル変数は使用しないでください」、「ランタイム型識別(RTTI)の使用は危険です」などです。これらのヒューリスティックのソースは何ですか?何が本当ですか?彼らはいつも本当ですか?このコラムでは、これらのヒューリスティックの根底にある設計原理、つまりオープンクローズド原理について調べます。

Ivar Jacobsonが述べたように、「すべてのシステムはライフサイクル中に変化します。最初のバージョンよりも長く続くと予想されるシステムを開発する場合、これを念頭に置いておく必要があります。」バートランドマイヤーは、1988年に有名なオープンクローズドプリンシパルを考案したずっと前に、私たちに指導をしてくれました。彼を言い換えると:

ソフトウェアエンティティ(クラス、モジュール、機能など)は、拡張のために開かれている必要がありますが、変更のために閉じられている必要があります。

プログラムへの単一の変更が従属モジュールへのカスケードの変更をもたらす場合、そのプログラムは、「悪い」デザインに関連するようになった望ましくない属性を示します。プログラムは壊れやすく、硬直し、予測不能で再利用不能になります。オープンクローズド原則は、これを非常に簡単な方法で攻撃します。それは、決して変わらないモジュールを設計するべきだと言っています。要件が変更された場合、すでに動作している古いコードを変更するのではなく、新しいコードを追加することにより、そのようなモジュールの動作を拡張します。

説明

開閉原理に準拠するモジュールには、2つの主要な属性があります。

  1. それらは「Open For Extension」です。
    これは、モジュールの動作を拡張できることを意味します。アプリケーションの要件の変更に応じて、または新しいアプリケーションのニーズを満たすために、モジュールを新しい異なる方法で動作させることができること。
  2. それらは「変更のためクローズ」です。
    このようなモジュールのソースコードは侵害されています。誰もソースコードを変更することはできません。

これらの2つの属性は互いに対立しているように見えます。モジュールの動作を拡張する通常の方法は、そのモジュールに変更を加えることです。変更できないモジュールは通常、動作が固定されていると考えられています。これら2つの対立する属性はどのように解決できますか?

抽象化が鍵です...


3
これは抽象化を説明する良い記事です。しかし、考慮すべき基本的なポイントがあり、それはそもそもレイアウトされた良い抽象的なデザインでしたか?多くのショップには、それを変更する唯一の方法は「拡張」ではなく「変更」である多くのレガシーコードがあります。これが事実である場合、おそらくそれを変更するために働くべきですが、それまでは、コードの変更で立ち往生しています。
マイケルK

@Chris、cool-この種のことが好きな場合は、ボブおじさんの "Clean code"本もお勧めします。
マルタインVerburg

@Michael-完全に同意します。これは、コードをリファクタリングして理想的なテスト可能にする必要があるようなものです。
マルタインVerburg

この記事は、抽象化の重要性を非常にうまく示しています。しかし、私は抽象化間の関係を把握しておらず、モジュールを書いた後にモジュールを決して変更しようとはしていません。抽象化とは、モジュールYを変更せずにモジュールXを変更できることを意味します。しかし、必要な場合は、モジュールXまたはモジュールYを変更できるようにすることは重要ではありませんか。
ウィンストンイーバート

1
ワオ。コードは違反ですか?私はボブおじさんの大ファンではありませんでした。このプリンシパルは教訓的で、非常に実用的ではなく、現実とのつながりが限られています。
user949300 14

12

ケイト・グレゴリーによって答えは非常に良いですが、新しい要件は、既存の比較的小さな変化で満足させることができる別の状況を検討しInvoiceたクラスを。たとえば、請求書PDFに新しいフィールドを追加する必要があるとしましょう。OCPによると、コードの数行を変更することで既存の実装に新しいフィールドを追加できる場合でも、新しいサブクラスを作成する必要があります。

私の理解では、OCPは80年代および90年代初期の現実を反映しており、プロジェクトではバージョン管理さえ使用されず、自動回帰テストや洗練されたリファクタリングツールの利点はほとんどありませんでした。OCPは、手作業でテストして本番環境に入れていたコードを壊すリスクを回避する試みでした。現在、動作中のソフトウェア(バージョン管理システム、TDDおよび自動テスト、リファクタリングツール)が破損するリスクを管理するためのより良い方法があります。


2
はい、実際には、すべてのメソッドを保護する(O / Cよりもはるかに重要なYAGNI原則に反し、違反する)場合を除き、すべての可能な未来に合わせて拡張できるクラスを作成することはできないためdito)。
マーティンウィックマン

「OCPによれば、数行のコードを変更することで既存の実装に新しいフィールドを追加できる場合でも、新しいサブクラスを作成する必要があります。」:本当にですか?新しいフィールドや新しいメソッドを追加してみませんか?重要な点は、既存のものを変更するのではなく、追加(拡張)するだけであることです。
ジョルジオ

標準ライブラリ/フレームワークを扱うとき、この原則は理にかなっていると思います。十分に確立されたコードを開いて変更する必要はありません。それ以外の場合は、定数のリファクタリングとテスト、テスト、テストがすべてです。
mastaBlasta

@Giorgio確かに、新しいフィールドやメソッドを追加すること、私はほとんどの場合、お勧めだろうか。しかし、それは拡張ではなく、「修正」です。OCPの要点は、「拡張のために開かれている」間、コードは「変更のために閉じられる」(つまり、既存のソースファイルに変更を加えない)ことです。OCPの拡張は、実装の継承によって実現されます。
ロジェリオ

@Rogério:クラスレベルで拡張と変更の境界を定義するのはなぜですか?これには特別な理由がありますか?メソッドレベルで設定したいです。メソッドを変更するとアプリケーションの動作が変わり、(パブリック)メソッドを追加するとインターフェイスが拡張されます。
ジョルジオ

6

個人的には、この原則はひとつまみの塩で取られるべきだと思います。コードは有機的であり、ビジネスは変化し、時間の経過とともにビジネスのニーズに応じてコードが変化します。

抽象化が重要であるという事実に頭を悩ませるのは非常に難しいと思います。抽象化が元々間違っていた場合はどうなりますか?ビジネス機能が大幅に変化した場合はどうなりますか?

この原則は、基本的に、設計のオリジナルの意図と動作が決して変わらないことを保証します。これはおそらく、パブリックAPIを使用し、クライアントが新しいリリースや他のいくつかのケースに追いつくのに苦労している人に有効です。ただし、会社がすべてのコードを所有している場合は、この原則に挑戦します。

コードのテストカバレッジを十分に確保すると、コードベースのリファクタリングが簡単になります。つまり、物事を間違えても大丈夫です-テストは、より良い設計に導くのに役立ちます。

テストがない場合、この原則は妥当です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.