定義
オープンクローズドプリンシプル(OCP)は、オブジェクト指向プログラミングで最も基本的かつ重要な設計原則の一つです。プログラムの設計では、いくつかのデザインパターンを使用し、関連する設計原則を遵守するために、プログラムが "オープンとクローズド "を達成することができるようにすることです!とは言っても、正確にオープンとクローズの原則は何ですか?定義は次のとおりです:
ソフトウェア・エンティティなどは拡張可能であるべきですが、変更可能であってはなりません。
一見、この定義は分かりにくいかもしれませんが、オープン・クローズという言葉は、実は分解することができます。
拡張に対してオープンであること:これは、システムのモジュール、クラス、メソッドが、新しい機能でシステムを拡張できるプロバイダーに対してオープンであることを意味します。
変更に対してクローズド: システムのモジュール、クラス、メソッドは、ユーザに対してクローズドであるべきであることを意味します。ユーザがこれらの関数を使用するとき、プロバイダが新しい関数を追加したので、それに応じて修正されることはありません。
オープン・クローズの原則を使う理由
- オープン性とクローズ性の原則は、オブジェクト指向プログラミングをする人なら誰でも開発中に口にするほどよく知られています。
- 開放と閉鎖の原則は最も基本的なものであり、開放と閉鎖の原則は6つのデザイン原則の精神的リーダーであると言えます。
- オープンとクローズの原則は、テストの効率を向上させるために、元のコードは、変更の実装の拡張を介してのみ、変更されません、テストに戻ってすべてのテストをテストする必要はありません。
- オープンとクローズの原則は、コードの再利用性と保守性を向上させます。
コードの実践
今、リンゴとバナナを売る果物屋があるとします。コードは次のようになります:
class FruitShop {
public sellFruit(fruit) {
if (fruit.type === 1) {
this.sellApple(fruit);
} else if (fruit.type === 2) {
this.sellBanana(fruit)
}
}
public sellApple(fruit) {
console.log('リンゴを売る, fruit);
}
public sellBanana(fruit) {
console.log('バナナを売る, fruit);
}
}
class Fruit{
public type;
}
class Apple extends Fruit {
Apple() {
super.type = 1;
}
}
class Banana extends Fruit {
Banana() {
super.type = 2;
}
}
仮に、果物屋が新しい品種「スイカ」を追加する必要があるとします。上記のサンプルコードによると、次のように調整する必要があります。
- スイカクラスを追加します:
class Watermelon extends Fruit {
Watermelon() {
super.type = 3;
}
}
- FruitShopクラスにスイカを買うメソッドを追加
public sellWatermelon(fruit) {
console.log('スイカを売る, fruit);
}
- FruitShopクラスのsellFruitメソッドを修正します。
public sellFruit(fruit) {
if (fruit.type === 1) {
this.sellApple(fruit);
} else if (fruit.type === 2) {
this.sellBanana(fruit)
} else if (fruit.type === 3) {
this.sellWatermelon(fruit)
}
}
果実を追加する必要性を達成するために、上記の3つのステップを介して、このアプローチは理解しやすいが、関数の変更時に、コードの変更量が特に大きくなります。そして、この方法は、"オープンとクローズの原則 "に沿っていません。
様々なフルーツクラスは「提供者」であり、FruitShopクラスはこれらのフルーツクラスを利用する「利用者」です。オープン・クローズの原則は、修正をオフにすることを指しますが、現在では、プロバイダが新しい機能を追加すると、ユーザーも修正する必要があり、この原則に違反しています。
上記のコードは次のように最適化できます:
class FruitShop {
public sellFruit(fruit) {
fruit.sell()
}
public sellApple(fruit) {
console.log('リンゴを売る, fruit);
}
public sellBanana(fruit) {
console.log('バナナを売る, fruit);
}
public sellWatermelon(fruit) {
console.log('スイカを売る, fruit);
}
}
class Fruit{
public type;
public sell() {
}
}
class Apple extends Fruit {
Apple() {
super.type = 1;
}
public sell() {
console.log('リンゴを売る');
}
}
class Banana extends Fruit {
Banana() {
super.type = 2;
}
public sell() {
console.log('バナナを売る');
}
}
この時点で、新しい果物が追加されたときに必要なのは、プロバイダがFruitクラスを継承する新しいサブクラスを追加することだけです。
class Watermelon extends Fruit {
Watermelon() {
super.type = 3;
}
public sell() {
console.log('スイカを売る');
}
}
これで新しい果物の開発は完了し、元のプログラムには手を加えず、Fruit クラスから継承したサブクラスのみを追加しました。こうすることで、元のコードの構造と整合性を守りつつ、バグが発生する可能性を減らしつつ、保守性を確保することができます。
上記の説明を通して、オープン・クローズの原則が、「既存のコードを修正して完成させるのではなく、モジュール、クラス、メソッドを通してソフトウェアを拡張し、機能的な変更を実現する」というものであることがお分かりいただけたと思います。そうすることで、コードの修正によってプログラムにもたらされるエラー率を大幅に減らすことができます。