Skip to content.

Sections
Personal tools
You are here: Home » コミュニティ » masarl memorial » homepage3.nifty.com » masarl » article » デザインパターンとOpen-Closed Principle

デザインパターンとOpen-Closed Principle

Document Actions

デザインパターンとOpen-Closed Principle

1999/06/11 石井 勝

概要

この記事では,まずOpen-Closed Principleの意味と解説を行い,その後デザインパターンをOpen-Closed Principleの観点から眺めます.デザインパターンのうちの多くはOpen-Closed Principleを満たすために用意されたものとみなすことができます.Open-Closed Principleを理解し,数あるデザインパターンの中から,どういう場合にどのパターンを使うのが一番効果的なのかを考えます.

目次

  1. はじめに
    1. ソフトウェアと連続性
    2. 仮想仕事の原理
    3. 修正と追加
  2. Open-Closed Principle
    1. Open-Closed Principleの例
    2. Open-Closed Principleの反例
    3. オブジェクト指向とOpen-Closed Principle
  3. デザインパターンとOpen-Closed Principle
    1. デザインパターンの例 - Iteratorパターン
    2. デザインパターンの選択 - ObserverとMediator
  4. さらに勉強したい人のために

はじめに

オブジェクト指向のコミュニティにおいてブームを起こしたGang of Fourのデザインパターンは,全部で23個ものパターンがあります.このデザインパターンは,フレームワークなどに繰り返しあらわれる設計を抽出したものですから,いうなればオブジェクト指向のエッセンスを集めたものだといえるでしょう.

オブジェクト指向には,カプセル化,継承,ポリモルフィズムといった数少ない道具しかありません.では,なぜ23個もの多くのパターンになってしまったのでしょうか? このことは,デザインパターンの中に何かかくされた原理というべきものが存在するということを暗示しています.それが今回紹介するOpen-Closed Principleです.

ここでは,Open-Closed Principleの意味と解説を行い,その後デザインパターンをOpen-Closed Principleの観点から眺めてみることにしましょう.実は,デザインパターンのうちの多くはOpen-Closed Principleを満たすために用意されたものと考えることができるのです.Open-Closed Principleを理解すれば,より効果的にデザインパターンを使うことができ,どういう場合にどのデザインパターンを使うのがよいか,より統一的な理解ができるようになるでしょう.

それでは,まず優れたソフトウェアとは何かということから始めましょう.

ソフトウェアと連続性

まず,ソフトウェアを仕様変更とそれにかかるコストという観点から眺めてみましょう.

大幅な仕様変更では,修正部分が多くなってそのためのコストがかさむのは当然のことです.では,少しの仕様変更があった場合はどうでしょうか?

うまく作られているソフトウェアはこの場合でも少しの変更ですみますが,よくないソフトウェアは,この少しの変更が大幅な修正を引き起こす場合があります.

つまり,よくないソフトウェアは仕様変更に対してそのコストが連続していない,不連続だ,ということを意味します.

優れたソフトウェアにするには,仕様変更に対して,連続性をもたせるようにプログラムします.変更が小さければコストも小さく,大きければそれなりにコストが大きくなる自然なソフトウェアが優れたものだといえるでしょう.少しの変更が多くの修正を引き起こし,ひいてはバグの温床になるのであればユーザから信頼されないのは当たり前のことです.

よくある例としては,ユーザの考えている仕様変更と実際のプログラムの仕様変更のギャップです.ユーザが考えている仕様変更が単純に見えても実際そうではないことが往々にしてありますが,なるべくならそういうギャップがないようなソフトウェアにするべきでしょう.

仮想仕事の原理

仕様変更や拡張性への感覚を鋭くすることがよいソフトウェアを設計するためのカギになります.そのためにちょっと別の話をしましょう.

物理学には解析力学という分野があります.解析力学では物体のつりあいを調べるときに「仮想仕事の原理」という手法を用います.仮想仕事の原理の考え方は,とても単純です.

対象がつりあっているということは,すこしくらい揺さぶってももとに戻る,つまり安定しているということを意味します.したがって,対象を仮に揺さぶってみて,その対象がどういう振る舞いをするか考えればその対象が安定しているかどうかわかります.

解析力学における仮想仕事の原理は,これを変分法という数学を用いて数式であらわし,揺らぎを「変分」としてあらわすことで,対象がどうなるのかを調べます.この変分を仮想変位と呼びます.

この考え方はソフトウェア開発においても有用です.

つまり,ある仕様変更,機能追加という仮想変位の中でソフトウェアはどういう影響をうけるのか.ほとんど修正がなく安定しているのか,それとも大幅な修正が必要でかなり不安定なのか.

このことを絶えず意識することで,そのソフトウェアの長所と短所がはっきりし,仕様変更に強いメンテナンスしやすいものになります.現実には,すべての仕様変更に強いソフトウェアはありえないでしょう.したがってもっとも起こる可能性の高い仕様変更を予想し,その仕様変更に対して十分安定したソフトウェアを作ることが必要なのです.

つまり,

「ソフトウェアがさまざまな仕様変更でどういう修正が加わるのかを常に考えること」

これが,優れたソフトウェアをつくるための第一歩なのです.

修正と追加

では,仕様変更があったときに,どう対応できれば一番いいのでしょうか?

バグを減らすためには,なるべく修正個所を押さえる必要があります.修正個所が広範囲にわたってしまうと,それだけでバグの可能性が多くなり,修正コストも大きくなってしまいます.

なるべくなら,修正個所は一個所にしぼりたいところです.共通する部分をうまく共有しているプログラムなら,このことを実現するのはそんなに難しくありません.

でも,これですべて終わりでしょうか?ほかにもっといい方法がないのでしょうか?

それは,コードの追加です.コードの修正ではなく,コードの追加だけで対応できれば,バグを生む可能性はかなり減ります.さらに,コードを修正する必要がなくなったモジュールは,再利用できるというメリットもあります.

コードの修正ではなく,コードの追加で変化に対応する -- これは,従来の構造化プログラミングでは簡単には実現できなかったことです.けれどもオブジェクト指向の道具である継承とポリモルフィズムを使えば,それが実現できます.

この「機能拡張をコードの修正ではなくコードの追加によって行う」というのが,次に述べるOpen-Closed Principleにしたがうソフトウェアがもつ最大の特徴なのです.

Open-Closed Principle

Bertrand Meyerによれば,Open-Closed Principleとは次のことを意味します.

「モジュールは拡張性について開いて(Open)おり,修正について閉じて(Closed)いなければならない」

このOpen-Closed Principle -- 「結んでひらいての法則」は,オブジェクト指向設計を考える際,その設計が正しいかどうかの指針を与えてくれるもっとも重要な原理です.開いているのに閉じている -- 一見矛盾しているこの原理は,一体何を意味しているのでしょうか? 順を追って説明しましょう.

あるモジュールについて,その機能を拡張できるとき,そのモジュールは「開いている」といいます.開いているモジュールはソフトウェアの機能追加,仕様変更に応じて異なった振る舞いをするようにできます.

モジュールは,将来どんな風に拡張されるかどうかは予想できません.したがってそのモジュールには柔軟性 -- 開いていることが要求されます.

また,あるモジュールが他のモジュールから利用でき,そのソースコードを修正することが許されないときそのモジュールは「閉じている」といいます.場合によってはそのモジュールはライブラリとして提供することもできます.

モジュールがひんぱんに修正されると,そのモジュールに依存しているほかのモジュールはその度に更新することになります.ソフトウェアが安定するためには,修正に対して閉じていることが要求されます.

開いているのに閉じている -- これはどうやったら実現できるのでしょうか?具体例を通じて説明しましょう.

Open-Closed Principleの例

ここでは,Open-Closed Principleの例として,次のような単純な音楽ソフトを考えましょう.このツールには,音符をあらわすNoteクラス,四分音符をあらわすQuaterクラス,二分音符をあらわすHalfクラスがあるとします.

class Note
{
public:
    virtual void Play() = 0;
};

class Quarter : public Note
{
public:
    virtual void Play();
};

class Half : public Note
{
public:
    virtual void Play();
};
  

これらの音符を集めて演奏する楽譜クラスStaffは,次のようになっているとします.

class Staff
{
public:
    void AddNote(Note*);
    void Play();
private:
    typedef std::vector<Note*> Notes;
    Notes mNotes;
};

void Staff::Play()
{
    Notes::iterator i;
    for (i = mNotes.begin(); i != mNotes.end(); ++i)
        (*i)->Play();
}
   

楽譜クラスStaffは,AddNoteメソッドで音符を追加していき登録された音符をPlayメソッドを使って演奏します.

さて,このStaff::Playメソッドは単純ですが,次のような意味でOpen-Closed Principleを満たしていることがわかります.

開いている(Open)

八分音符や四分休符などの音符を追加するという機能追加に対して拡張性がある

閉じている(Closed)

同様に,八分音符や四分休符などの音符を追加するという機能追加においてまったく修正する必要がない.

このように,Staff::Playの振る舞いを拡張するには,コードの修正ではなくコードの追加で十分です.つまり,Staff::PlayはOpen-ClosedPrincipleを満たしていることがわかりました.

Open-Closed Principleの反例

今度は,同じ音楽ソフトをC言語を使って実装してみましょう.C言語の構造体を使って音符オブジェクトをあらわし,列挙型NoteTypeでどんな種類の音符なのか判断できるようにします.

enum NoteType { Quarter, Half };

typedef struct
{
    NoteType mNoteType;
    double   mPitch; //音程
}Note;

void Quarter_Play(Note* self);
void Half_Play(Note* self);
  

同様に,楽譜オブジェクトも構造体であらわします.

typedef struct
{
    Note** mNotes;
    int    mNoteCount;
}Staff;

void Staff_AddNote(Staff* self, Note* aNote);

void Staff_Play(Staff* self)
{
    int i;
    Note* aNote;
    
    for (i = 0; i < self->mNoteCount; ++i) {
        aNote = self->mNotes[i];
        switch(aNote->mNoteType) {
        case Quarter:
            Quarter_Play(aNote);
            break;
        case Half:
            Half_Play(aNote);
            break;
        }
    }
}
  

関数Staff_AddNoteで音符を追加し,Staff_Playで演奏をします.

上の関数Staff_Playは,明らかにOpen-Closed Principleを満たしていません.つまり,八分音符や四分休符などの音符オブジェクトを追加したいときに,コードを修正しなければならないのです.

確かに上の例ではその修正は簡単ですが,ある程度規模が大きいプログラムではこのような関数がプログラムのあちこちに現われる可能性があります.修正個所が分散し,修正するたびにすべて更新する必要があるのならバグを生む可能性はかなり高くなります.

また,この例ではなぜswitch文がオブジェクト指向プログラミングで好まれないのかということも示唆しています.switch文の分岐を変更するような要求に対して,Open-Closed Principleが満たされていないからなのです.

オブジェクト指向とOpen-Closed Principle

さて,ここまでの説明でOpen-Closed Principleとはどんなものかわかっていただけたかと思います.

これは,別にオブジェクト指向だからだ,ということではありません.オブジェクト指向が目指すべき一つの目標です.けれどもオブジェクト指向の場合,Open-Closed Principleを満たすソフトウェアモジュールを作るための道具が標準で用意されています.継承とポリモルフィズムです.

オブジェクト指向では,オブジェクトとそのひな型であるクラスが主役です.オブジェクト指向のソフトウェアは,複数のオブジェクトが複雑にメッセージを送りあうダイナミックなシステムであると考えられます.ここでは,もっと単純化して次のようなオブジェクト指向のモデルを考えましょう.

1) 単純なモデル

それでは,[図A]を見てください.

図A

このクラス図では,ClientAクラスとClientBクラスはServerクラスを使って,それぞれServerにメッセージを送っています.

この単純なモデルは,Open-Closed Principleを満たしているでしょうか?ちょっと考えてみましょう.

開発が進むつれ,ClientBクラスはいままでのServerクラスでは要求が満たされないことがわかりました.つまり,Server' クラスというServerと大部分が同じで振る舞いが異なるクラスが必要になってしまいました.このことに対応するため,例えば[図B]のような設計します.

図B

新しい設計では,バージョンが異なる2つのServerクラスがあります.これはコピー&ペーストプログラミングの典型例です.このプログラミングスタイルがよくないのは明らかでしょう.つまり,微妙に異なるServerへの仕様変更に対して,上のモデルはOpen-Closed Principleを破っていることになるのです.

2) 継承モデル

次に,ClientとServerの間にクラスをはさんで考えてみましょう[図C].

図C

AbstractServerクラスは,抽象クラスで,Serverクラスのスーパークラスになっています.

再び上のモデルに対してServer'が必要になった場合を考えましょう.今度は,[図D]のようになります.

図D

このとき,ClientAクラスとAbstractServerはまったく修正されていません.また,Server'への機能追加(あるいは変更)をコードの修正ではなくコードの追加によって実現しています.

したがって,上のモデルはOpen-Closed Principleを守っているといえるでしょう.以上から,オブジェクト指向でOpen-Closed Principleに対応するためには,

  1. 変更の可能性があるところを見つける
  2. その部分を抽象化してクラスにしてしまう.
  3. 抽象クラスのサブクラスで変化に対応する

とすればよいことがわかります.これがOpen-Closed Principleを満たすオブジェクト指向ソフトウェアの戦略だといえます.

デザインパターンとOpen-Closed Principle

Open-Closed Principleは,まず予想される仕様変更などに注目することが必要です.それでは,各デザインパターンはどうなっているのでしょう?変更の観点からデザインパターンを見ると,多くのデザインパターンは,変更に柔軟に対応するために用意されたものだということがわかります.次の[表A]を見てみましょう.

表A: 変更からみたデザインパターンの例
変更箇所 パターン
アルゴリズム Strategy, Visitor
オブジェクトの状態 State
オブジェクトの振る舞い Decorator
インターフェイス Adapter
実装方法Bridge
オブジェクト間の通信方法 Mediator
コンテナクラスのアクセス方法Iterator

このように,各デザインパターンは何に対して柔軟性をもつかということが意図されている,といえます.さらに多くのパターンは柔軟性を持たせるクラスそのものに対応する名前をもっています.

こういった変更部分そのものは,あまり現実世界に対応するオブジェクトが存在しているとはいえません.一方で,オブジェクト指向の利点は現実世界のオブジェクトに対応したオブジェクトを作ることができる,だからモデル化しやすい,とよく言われます.しかし,それだけではオブジェクト指向の利点の半分をいっているにすぎないのです.

オブジェクト指向のもう一つの大きな利点は「抽象化」です.オブジェクト指向を使えば,抽象化された概念をそのままコードに埋め込んでプログラムすることができます.

けれども,抽象的な概念というのはなかなかわかりにくいものです.数学では,そういった概念に明確な名前を与えて,考えやすくするようにしています.これと同じ意義がデザインパターンにもあるのです.そう,デザインパターンで重要なのは概念に名前を付けたということなのです.

デザインパターンには,何も新しいことはありません.デザインパターンの中でよいパターンの条件とは,

  • 古いパターン
  • 何度も使われているパターン
  • シンプルなパターン

なのです.デザインパターンは,新しいテクノロジーではなく,新しいものごとのとらえ方なのです.抽象的な概念をデザインパターンでとらえることにより,普通ならなかなか思い付かないクラス設計をすることができるようになるのです.

このように,どんな修正に対して柔軟性を持たせるようにデザインパターンが用意されているかということに注目すれば,かなりわかりやすくなります.デザインパターンは抽象的すぎてよくわからないといった方は,こういったOpen-Closed Principleの視点からデザインパターンを眺めてみることをおすすめします.

デザインパターンの例 - Iteratorパターン

ここでは,Open-Closed Principleがデザインパターンにどう関わっているかをみるため,Iteratorパターンをとりあげましょう.

リンクリスト,動的配列などのデータ構造をあつめたコンテナクラスライブラリには,必ずといっていいほどイテレータというクラスが存在します.このイテレータはどうして必要になったのでしょうか?Open-Closed Principleの立場から考えてみましょう.

まず,出発点としてリンクリストが与えられ,これをシーケンシャルにアクセスしたいと考えたとしましょう.一番最初の設計では,カプセル化を考えて次のようなクラスになるでしょう:

class Item;

class LinkList
{
public:
    void AddItem(Item*);
    void RemoveItem(Item*);

    // シーケンシャルアクセスのためのメソッド
    Item* FirstItem();
    Item* NextItem();
    bool More() const;
};
  

このLinkListクラスを利用した関数は,例えば次のようになります.

void TraverseList(LinkList& aLinkList)
{
    for (Item* i = aLinkList.FirstItem();
         i = aLinkList.NextItem();
         aLinkList.More()) {

        cout << i->Name() << endl;
    }
}
  

その後,このリンクリスト内のオブジェクトをランダムアクセスする必要がでました.このランダムアクセスを実現するため,配列にしたいという仕様変更が発生したのです.

このとき関数TraverseListはOpen-Closed Principleを満たしていません.つまり,コンテナクラスの種類を変更するということに関して拡張性がなかったのです.そこでOpen-Closed Principleを満たすようにするため,次のように修正しました.

class Item;

class Container
{
public:
    void AddItem(Item*);
    void RemoveItem(Item*);

    Item* FirstItem();
    Item* NextItem();
    bool More() const;
};

class LinkList : public Container {...};
class Vector : public Container {...};

void TraverseContainer(Container& aContainer)
{
    for (Item* i = aContainer.FirstItem();
         i = aContainer.NextItem;
         aContainer.More()) {
        cout << i->Name() << endl;
    }
}
  

このように,Containerクラスという変更部分を抽出したクラスを作ることで,あとはどんな種類のコンテナクラスが来ても拡張はコードの追加で実現できるようになりました.動的配列は,Containerクラスのサブクラスとして追加すれば,関数TraverseListはまったく修正する必要がありません.コンテナクラスの変更という仕様変更に対してOpen-Closed Principleを満たしています.

さて,また仕様変更が生じました.こんどは,TraverseContainerを使うとき,逆方向にスキャンしたり,順方向にスキャンしたりしてコンテナクラスのアクセス方法をいろいろ変えたいと考えました.これを次のように修正しまっていいのでしょうか?:

class Container
{
public:
    ...
    void SetForward();   //順方向にスキャンする
    void SetBackward();  //逆方向にスキャンする
    ...
};
  

これは,明らかにアクセス方法という仕様変更についてOpen-Closed Principleを満たしていませんね.そう,今度は,こういうアクセス方法を抽象化してイテレータというクラスに分離すればよいわけです:

class Iterator
{
public:
    Iterator(Container&);
    Item* FirstItem();
    Item* NextItem();
    bool More() const;
};

class FowardIterator : public Iterator {...}
class BackwardIterator : public Iterator {...}
  

このようにアクセス方法を別クラスにすることで,いろいろなアクセス方法に対応できるようになりました.実際には,各イテレータをLinkListやVectorに対応するためもう少し複雑になりますが,大体の趣旨はわかっていただけたかと思います.このように,Open-Closed Principleへの進化の形がデザインパターンと見ることができるわけです.

デザインパターンの選択 - ObserverとMediator

オブジェクト指向モデルでは,複数のオブジェクトが互いにメッセージを送って協調しあうダイナミックなものだといいました.けれども,これを素直にうけとめてモデル化すると,あちこちで処理が分散し,結果的にわかりにくくメンテナンスしにくいものになってしまいます.

クラスやオブジェクトの概念を理解したばかりの初心者は,オブジェクト間のメッセージのやりとりをそのまま実装してしまい,結果として各オブジェクトが他のオブジェクトに密接に関連しあう複雑なモデルにしてしまいがちです.これではそもそも何のためにクラスやオブジェクトに分けたのかわからないですね.分けないほうがまだましかもしれません.

図E

デザインパターンの中には,そういったオブジェクト間のやりとりをうまく扱うものがちゃんとあります.代表的なものはObserverとMediatorでしょう.ここでは,どういうときにどんなパターンを使えばいいのかObserverとMediatorパターンを比較して考えてみましょう.

Mediatorパターンは,オブジェクト間のやりとりそのものをカプセル化してクラスにしてしまうというものです.このことによってオブジェクト間のやりとりに秩序ができ,メンテナンスしやすいものになります.

図F

Mediatorパターンが採用されたシステムでは,各オブジェクトが個別に通信せずに,もっぱらMediatorを経由してメッセージ通信を行います.このことによってシステムがわかりやすくなります.

それではこのパターンは,Open-Closed Principleを満たしているでしょうか?ちょっと考えてみましょう.上のモデルにおいて,変更の可能性があるのは協調しているオブジェクト間のやりとりと,協調オブジェクトの追加・削除ですね.

Mediatorパターンは,こういった変更に対して明らかにOpen-Closed Principleを満たしていません.特に協調オブジェクトの追加と削除には,Mediatorクラスの内部を修正することしか変化に対応できないのです.

では,このパターンはよくないパターンなのでしょうか? 一概にはそう言い切れません.少なくともそういう変化に対して修正個所を一個所に閉じ込めるという利点があります.あえていうなら,弱いOpen-Closed Principleを満たしている,といえるでしょう.その意味でMediatorパターンはあまりオブジェクト指向的であるとはいえないですが,妥協点としてはいいパターンになる場合があります.

次に,Observerパターンを見てみましょう.

図G

Observerパターンは,一対多の依存関係がある場合に使われます.Observableオブジェクトがまずあって,それに依存しているオブジェクトObserverを自動的に更新するような処理がある場合によく使われるパターンです.

再びこのパターンがOpen-Closed Principleを満たしているかどうか見てみましょう.Observableに対して,Observerを新しく追加する仕様変更を考えます.このとき,Observableと他のObserverはまったく影響をうけません.つまり,Observerを追加するという仕様変更に対してはOpen-Closed Principleを満たしているといえるわけです.

さて,ObserverとMediatorパターンの特徴がわかったところで,フォントダイアログを作る例を考えましょう.このダイアログには,コンポーネントが並んでいて,フォントの種類を指定するフォント・リストボックス,フォントの大きさを指定するフォントサイズ・リストボックス,ユーザにフォントがどんなものになるのか表示するフォントのサンプル表示ビューなどがあります.フォント・リストボックスの中にあるフォントを選択したら,フォントサイズ・リストをそのフォントに対応するフォントサイズが並ぶよう変更し,フォントのサンプル表示をそのフォントが表示されるように更新して... という具合に処理を行うとします[図H].

図H

このとき,まず第一案としては,フォント・リストボックスをObservable, フォントサイズ・リストボックスとフォントサンプル表示ビューををそのObserverにするということが考えられます.つまり,フォントが変わったときフォントサイズの種類とフォントのサンプル表示が変わらないといけないので,Observerのパターンを応用するわけです.

こうして,実際にObserverパターンを適用したとしましょう.そうすると,まず各コンポーネントにObserverとしての機能をもたせないといけません.また,サンプル表示ビューはフォントサイズにも依存しているので,この2つのコンポーネント間にもObserverパターンを使うことになってしまいます.ちょっと考えただけでもこれではずいぶん複雑です.

この場合は,Mediatorパターンにしてしまった方がシンプルだしメンテナンスもしやすいのです.

その理由は簡単です.この例では「1対多の依存関係の方が変化しやすい」ということだからです.コンポーネント間の依存関係が将来変更されやすいのに,今現在の仕様で各コンポーネントがObserver-Observableの関係があるからといって安易にObserverを使ってはいけません.この場合は,コンポーネント間の依存関係が変化しやすいので,それに対応したMediatorパターンを使うのがよいのです.別にObserverだからいい,というような発想ではなく,将来の変更に対応できるよう柔軟に設計するのがいいのです.

このように,デザインパターンを効果的に使うためには次のように考えるのが間違った使い方をしないための指針となるでしょう.

  • 変更の可能性があるのはどこか? 柔軟性を持たせなければならないところはどこか?

例えば,柔軟性を持たせなければならないのがアルゴリズムならStrategyのパターン,オブジェクトの状態ならばStateのパターンなどを検討してみます.

  • そのパターンを使うことで柔軟性を失うのはどこか?

例えば,AbstractFactoryパターンを使うと生成するオブジェクトのセットをごそっと変えることが可能ですが,そのセットに新しい種類のオブジェクトを追加するためにはかなり修正しなくてはなりません.つまり,オブジェクトの種類を変更することについてこのパターンはOpen-Closed Principleを満たしていないことがわかります.

このように,Open-Closed Principleがもっとも効果的に成り立つようにデザインパターンを使っていくことが重要です.デザインパターンには長所と短所両方あり,それらを見極めて使わなければなりません.自分が好きなパターンだからといって何度も同じパターンを使うのは単なるワンパターンにすぎません.

こういった意味で,デザインパターンは従来のアルゴリズムとデータ構造に似ているでしょう.アルゴリズムとデータ構造には長所と短所があります.例えば,あるアルゴリズムはすごく速く処理できるがその分メモリをたくさん消費します.また,別のデータ構造を使うと検索は速いが更新は遅いということがあります.同様に,なかなかすべてがうまくようなパターンはありません.どういう状況で使われるのか判断することが必要です.

結局,ソフトウェア開発にはこういったトレードオフが存在しそれを見きわめて設計することが要求されます.これはデザインパターンとて同じことです.

ここでは,そのトレードオフの指針としてOpen-Closed Principleを紹介しました.そのソフトウェアはOpen-Closed Principleを満たしているか? -- 優れたソフトウェアを作りたいと思っている方はこういった視点から自分のソフトウェアを眺めてみるのはいかがでしょうか.

さらに勉強したい人のために

Open-Closed Principleについては,何といってもまずBertrand Meyerの次の本:

Object-Oriented Software Construction, Second Edition Prentice Hall 1997, ISBN 0-13-629155-4

でしょう.この本はかなりのボリュームがあります.

記事としてはRobert C. MartinのC++ Reportの連載記事の一つである

The Open-Closed Principle, C++Report, Jan, 1996

がいいでしょう.これはObject MenterのWebサイトhttp://www.oma.com/Publications/publications.htmlから入手できます.この記事のほかに,彼はOpen-Closed Principleからはじまるオブジェクト指向設計のPrincipleを10個ほど集めて解説しています(Principles of OOD).それらを当たってみるのもいいかもしれません.同じWebサイトから入手できます.

Open-Closed Principleは,同じくRobert C.Martinの本:

Designing Object Oriented C++ Applications using the Booch Method Robert C.Martin, Prentice Hall, 1994, ISBN 0-13-203837-4

にも書かれています.でも彼の新しい本が年末(99年)までに出るといううわさなので, この本を買うのはもう少し待ったほうがいいかもしれません.

参考文献

  • E.Gamma,R.Helm,R.Johnson,and J.Vlissides, "Design Pattens: Elements of Reusable Object-Oriented Software", Addison-Wesley,1995
  • B.Meyer, "Object-Oriented Software Construction", Second Edition Prentice Hall 1997
  • R.C.Martin, "The Open-Closed Principle", C++Report,Jan,1996
  • R.C.Martin, "Designing Object Oriented C++ Applications using the Booch Method",Prentice Hall, 1994
  • D.Roberts and R.Johnson, "Patterns for Evolving Frameworks", Pattern Languages of Program Design 3,Chap26,Addison-Wesley,1997
  • R.Johnson "Software Pattens",ICSE98
  • R.Johnson "Using Design Patterns to Construct Frameworks",ICSE98