この記事は、2003年にメルマガ「週間オブジェクト倶楽部」に書いていた記事を若干編集・修正してQiitaに転載するものです。
ソフトウェア原則[1] - OCP(Open-Close Principle)
週刊オブジェクト倶楽部 2003-01号の書評『オブジェクト指向入門』の中で、
OCP"Open-Close Principle"として、
「ソフトウェアモジュールは、変更に対して閉じており、拡張に対して開いているべき」
を紹介しました。第1回目は、このOCP からです。次のC言語コードを見てどう思いますか?
typedef struct { int x, y; } Point;
typedef struct { Point p1, p2; } Line;
typedef enum { LINE, POINT } Kind;
typedef union { Point* point; Line* line; } ShapeUnion;
typedef struct {
Kind kind;
ShapeUnion of;
} Shape;
#include "shape.h"
void draw(Shape* shape) {
switch(shape->kind) {
case LINE:
/* draw line (shape->of->line.p1 ---- shape->of->line.p2) */
break;
case POINT:
/* draw point (shape->of->point)*/
break;
}
}
これは、私が15年前に書いたコードです。なかなかイケてるでしょ?
(編集注: 現在から26年前になってしまった。。。特に、union を of で参照しているのが気に入っていました)
でも、Kindとして新たにCIRCLEを追加したとき、このdraw関数を修正し、 case CIRCLEを追加しなければならいのです。
では、どうするの?C++では、
class Shape {
public:
virtual void draw() = 0;
};
#include "shape.h"
class Point : public Shape {
int x, y;
public:
void draw() { /* draw point (x,y) */ }
};
#include "shape.h"
class Line : public Shape {
Point p1, p2;
public:
void draw() { /* draw line p1-p2 */ }
};
と書くことができます。このようにすると、Circleの追加は、「既存のプログラムを全く変更せずに」(再コンパイルさえも!)できるのです。これが、 CloseしているプログラムをOpenにするというオブジェクト指向プログラミング の1つの真骨頂なのです。
- 修正の論理を、追加の論理に変換している
といえます。
ちなみに、オブジェクト指向プログラミングでは、構造化プログラミングで gotoが悪だったように、switch分岐は悪と言われています。 (2箇所以上同じ分岐がある場合)。
後日談(編集注:11年前の後日談です)。
現在では、『リファクタリング』という再設計技術が注目されるようになり、 コード1をコード2に再設計することを、Replace Conditional With Polymorphism (多態による条件分岐の置き換え)と呼んでいます。また、switch文が必ずしも 悪という訳ではなく、同じパターンのswitchが繰り返されることが悪だ、と いう解釈になっています。(Once and Only Onceの原則)
(編集時追加)参考: