【SOLID】開放閉鎖原則~拡張に開いていること、修正に閉じていること

open-closed-principle
開放閉鎖原則

Open-Closed Principle

拡張に対して開いていること、修正に対して閉じていること

ソフトウェアの拡張性についての原則です。この原則には様々な解釈があります。

SOLID原則と呼ばれる、主要5原則のひとつ。

SOLID原則
目次

どんなときに使える?

拡張性を考えるとき

リーダーさん

あとでこのクラス拡張するから、改修しやすくしとかないと…

というような場合ですが、この原則、正直使いどころが難しいです。

この原則の意味するところは?

開いている/閉じているとは?

意味するところは、

  • 開いている コードに機能を拡張できること
  • 閉じている 既存コードの修正をすることなく、コードの追加のみで拡張できること

一見、あたりまえ。コードを修正すれば機能追加できるし、他のところを直さなければよいし。

拡張とは何か?

プログラミングにおける「拡張」とは、プログラムを修正し、なにかの動作を加えること。

拡張のとき、修正箇所が少ないほうが、動作がおかしくなりにくい。(直していないところはそのまま動くわけだから)

この直す範囲を区切るのに、モジュールという考えがあります。ソースファイル、クラス、メソッド…とにかく意味的に機能を分割したものが、モジュールです。

モジュールA、モジュールB、があったとします。拡張するときに、モジュールAだけ直すので済むなら、モジュールBは前のまま動くわけです。

開放閉鎖原則が登場するとき

それならモジュールAとBを、うまく分けておくだけで、いいじゃない?と思います。

ただBの呼び方によって、こういう場合があります。

  • とある場合によって、呼ぶモジュールBの種類を変えたい
  • しかもその条件が外から与えられる

この場合、Bを呼ぶのはAだから、呼ぶものが変わればAも修正が必要。だけどAがやることは本質的に変わらないので変えたくない。

ポリモーフィズムの例

呼ばれるほうは変わるが、呼ぶほうは意識しないですむ仕掛け。ポリモーフィズムといいます。

例えば以下のような場合。

// 図形構造体(四角形や円)
struct Square
{
  ShapeType itsType;
  double itsSide;
  Point itsTopLeft;
};
// 全ての図形を描画する
void DrawAllShapes(Shape* list[], int n)
{
	int i;
	for (i=0; i<n; i++)
	{
		struct Shape* s = list[i];
    // 図形のタイプで分岐
		switch (s->itsType)
		{
		case square:
      // 四角形なら
			DrawSquare((struct Square*)s);
			break;
		case circle:
      // 円なら
			DrawCircle((struct Circle*)s);
			break;
		}
	}
}

DrawAllShapes は、Shapeの配列を受け、各々の描画を行います。

Shapeの型の情報を調べてはそれにふさわしい関数を呼び出します。(DrawCircle または DrawSquare)

いま、四角形と円がありますが、ここに三角形を追加するとします。するとDrawAllShapesに、三角形の図形描画を追加する必要あります。

続いて以下の例です。

// 図形クラス
class Shape
{
public:
	virtual void Draw() const = 0;
};
// 矩形クラス
class Square : public Shape
{
public:
	virtual void Draw() const;
};
// 円クラス
class Circle : public Shape
{
public:
	virtual void Draw() const;
};
// 全ての図形を描画する
void DrawAllShapes(vector<Shape*>& list)
{
	for (vector<Shape*>::iterator i=list.begin(),i!=list.end(); ++i)
		(*i)->Draw();
}

こうすると、三角形を追加したときに、必要なことはShape クラスに対し三角形クラスを追加するだけです。

DrawAllShapes 関数そのものは変更する必要はないわけです。

この原則ははたして使えるのか?

…と、開放閉鎖原則では、たいていこのようなポリモーフィズムがメリットとしてあげられます

これ、「なるほど!」と、「は?」と、意見が分かれるのでないでしょうか。

確かにDrawAllShapesを直さなくて済むのはわかる。でも、

新人くん

直す前のほうが、やっていること分かりやすいし直しやすそう…

それに、将来に三角形を追加するといったって、まず絶対に四角か円しかないんだけど…という場合もあるのです。

そういう場合に、無理して直す必要あるのか?

そもそも将来の予測はかなり困難です。分かりもしない将来のために仕掛けつくるより、シンプルなほうがよい場合も多いですYAGNI原則ですね。

まとめ

開放閉鎖原則は、オブジェクト指向を学んだ人には、ちょっと甘美な響きに聞こえます。

「コードを追加するだけで拡張できるんだぜ!オブジェクト指向の真骨頂だ!」

よいときももちろんあります。ただ「余計なコードを埋め込むための免罪符」になってしまうときもあります。

ほんとうにその拡張は必要なのか、よく考えて、適切な対応を決めましょう。

SOLID原則
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次