オープン/クローズド原則の背後にある意味と理由は何ですか?
-
09-06-2019 - |
質問
オープン/クローズ原則では、ソフトウェア エンティティ (クラス、モジュールなど) は拡張に対してはオープンであるが、変更に対してはクローズされるべきであると規定されています。これは何を意味しますか?また、なぜそれが優れたオブジェクト指向設計の重要な原則であるのでしょうか?
解決
具体的には、コードを書き直すことなく (場合によっては再コンパイルしなくても) 将来の予期せぬ変更をサポートできるように、エンティティを (個々の設計を通じて、またはアーキテクチャへの参加を通じて) 十分に拡張できるようにするという OOP 設計の「聖杯」についてです。 **)。
これを行う方法には、ポリモーフィズム/継承、合成、制御の反転 (別名: 制御の反転) などがあります。DIP)、アスペクト指向プログラミング、ストラテジー、ビジター、テンプレート メソッドなどのパターン、および OOAD の他の多くの原則、パターン、および手法。
** 6 つの「パッケージ原則」を参照してください。 REP、CCP、CRP、ADP、SDP、SAP
他のヒント
これは、新しいコードを新しいクラス/モジュールに配置する必要があることを意味します。既存のコードはバグ修正の目的でのみ変更する必要があります。新しいクラスは、継承を通じて既存のコードを再利用できます。
オープン/クローズの原則は、新しい機能を導入する際のリスクを軽減することを目的としています。既存のコードを変更しないため、コードが壊れることはありません。メンテナンスコストを削減し、製品の安定性を高めます。
これは、基底クラスに対する一見無害な変更が、以前の動作に依存していた継承者に予期せぬ結果をもたらす可能性があるという脆弱な基底クラスの問題に対する答えです。したがって、派生クラスが基本クラスによって定義された規約に従うように、依存したくないものを慎重にカプセル化する必要があります。そして、継承者が存在すると、次のようになります。 本当に 基本クラスで何を変更するかには注意してください。
DaveK より具体的には、通常、機能を追加したり、クラスの機能を変更したりする場合、元のクラスを変更する代わりにサブクラスを作成することを意味します。こうすることで、親クラスを使用する人は、後で親クラスが変更されることを心配する必要がなくなります。基本的には、下位互換性がすべてです。
オブジェクト指向設計のもう 1 つの非常に重要な原則は、メソッド インターフェイスを介した疎結合です。加えたい変更が既存のインターフェースに影響を及ぼさない場合は、変更しても非常に安全です。たとえば、アルゴリズムをより効率的にするためです。オブジェクト指向の原則も常識的に調整する必要があります:)
ソフトウェア エンティティは拡張に対してオープンである必要がありますが、変更に対してはクローズされている必要があります
つまり、クラスやモジュールは、そのまま使用したり、拡張したりできるが、変更は必要ない方法で作成する必要があります。
JavaScript での悪い例
var juiceTypes = ['Mango','Apple','Lemon'];
function juiceMaker(type){
if(juiceTypes.indexOf(type)!=-1)
console.log('Here is your juice, Have a nice day');
else
console.log('sorry, Error happned');
}
exports.makeJuice = juiceMaker;
ここで、別の Juice タイプを追加したい場合は、モジュール自体を編集する必要があります。これにより、 OCP が壊れます。
JavaScript の良い例
var juiceTypes = [];
function juiceMaker(type){
if(juiceTypes.indexOf(type)!=-1)
console.log('Here is your juice, Have a nice day');
else
console.log('sorry, Error happned');
}
function addType(typeName){
if(juiceTypes.indexOf(typeName)==-1)
juiceTypes.push(typeName);
}
function removeType(typeName){
let index = juiceTypes.indexOf(typeName)
if(index!==-1)
juiceTypes.splice(index,1);
}
exports.makeJuice = juiceMaker;
exports.addType = addType;
exports.removeType = removeType;
同じモジュールを編集せずに、モジュールの外部から新しいジュース タイプを追加できるようになりました。
この原則は、既存の安定したテスト済みの機能を変更することなく、新しい機能を簡単に追加でき、時間とコストの両方を節約できることを意味します。
多くの場合、インターフェースの使用などの多態性は、これを達成するための優れたツールです。
OCP に準拠するための追加の経験則は、派生クラスによって提供される機能に関して基本クラスを抽象化することです。あるいは、Scott Meyers が「非リーフクラスを抽象化せよ」と言っているように。
これは、基本クラスに実装されていないメソッドがあり、それらのメソッドをそれ自体がサブクラスを持たないクラスにのみ実装することを意味します。この場合、基本クラスのクライアントは、基本クラスには特定の実装が存在しないため、それに依存できません。
「オープン/クローズド」は、オブジェクト指向プログラミングでは明らかに便利ですが、開発のあらゆる側面で使用するのに健全な方法であることを強調したいと思います。たとえば、私自身の経験では、プレーン C を使用する場合は、できるだけ「Open/Closed」を使用することが大きな痛み止めになります。
/ロバート
これは、OO ソフトウェアはその上に構築されるべきですが、本質的に変更されるべきではないことを意味します。これは、基本クラスからの信頼性が高く、予測可能なパフォーマンスを保証するため、優れています。
私は最近、この原則が何を意味するかについて追加のアイデアを与えられました。オープンクローズ原則は、コードを記述する方法と、回復力のある方法でコードを記述した最終結果を同時に説明します。
私は、Open/Closed を 2 つの密接に関連する部分に分けて考えるのが好きです。
- コード、つまり 開ける 変更するには、入力を正しく処理するために動作を変更するか、新しい使用シナリオに対応するために最小限の変更が必要です。
- コード、つまり 閉まっている 新しい使用シナリオに対処するために変更を加える場合、人間の介入はほとんど必要ありません。単純にニーズが存在しないのです。
したがって、オープン/クローズ動作を示す (または必要に応じてオープン/クローズ原則を満たす) コードは、当初の目的を超えて使用シナリオに応じて最小限の変更を行うか、まったく変更する必要がありません。
実装に関して言えば?一般的に述べられている解釈「オープン/クローズは、コードが多型であることを指します!」せいぜい不完全な声明になること。コード内のポリモーフィズムは、この種の動作を実現するための 1 つのツールです。継承、実装...実際、すべてのオブジェクト指向設計原則は、この原則が示唆する方法で復元力のあるコードを作成するために必要です。
デザイン原則では、SOLID – 「SOLID」の「O」はオープン/クローズの原則を表します。
オープンクローズ原則は、クラス、モジュール、関数は拡張に対してはオープンであるが、変更に対してはクローズされるべきであるという設計原則です。
この原則は、既存のコード (テスト済みコード) への最小限の変更で新しい機能を追加する方法でコードの設計と記述を行う必要があることを示しています。設計は、既存のコードをできる限り変更せずに、新しい機能を新しいクラスとして追加できるように行う必要があります。
オープンクローズ設計原則の利点:
- すでにテストされたクラスを変更しないため、アプリケーションはより堅牢になります。
- 新しい要件にも簡単に対応できるため、柔軟性が高くなります。
- テストが簡単で、エラーが発生しにくくなります。
これに関する私のブログ投稿:
http://javaexplorer03.blogspot.in/2016/12/open-closed-design-principle.html