More C++ Idioms/インタフェースクラス(Interface Class)
インタフェースクラス(Interface Class)
[編集]意図
[編集]- クラスのインタフェースをその実装から分離する。
- 実行時多態的に抽象/クラスの実装を呼び出す。
別名
[編集]動機
[編集]クラスのインタフェースをその実装から分離することは、よい品質のオブジェクト指向ソフトウェア設計/プログラミングにとって基盤となるものである。 オブジェクト指向プログラミングに対して、分離の原理的な機構がインタフェースクラス(Interface Class)である。 しかし、C++ では(例えば Java などと比較して)、そのような分離を表現する専用の機構を提供していない。 Java では、interface キーワードが、抽象が持つべき public なメソッドのみを定めるために使われる。 C++ はそのようなキーワードを持たないが、インタフェースクラス(Interface Class)イディオムを使うことで、その機能性を大体表現することができる。 抽象の public なメソッドのみを表現し、実装を一切提供しないという発想である。 実装の欠如は、インタフェースクラスのインスタンスが存在できないということも意味する。
解法とサンプルコード
[編集]インタフェースクラスは、仮想デストラクタと、純粋仮想関数のみを含む。 これにより、他の言語(例えば Java)の interface と似た構成を提供する。 インタフェースクラスは、例えば基本クラス中の純粋仮想関数宣言のような多態的なインタフェースを定めるクラスである。 クラス階層を使うプログラマは、基本クラスを通じて、階層中のクラスのインタフェースのみとやりとりすることでクラス階層を使うことができる。
class shape // インタフェースクラス
{
public:
virtual ~shape();
virtual void move_x(int x) = 0;
virtual void move_y(int y) = 0;
virtual void draw() = 0;
//...
};
class line : public shape
{
public:
virtual ~line();
virtual void move_x(int x); // implements move_x
virtual void move_y(int y); // implements move_y
virtual void draw(); // implements draw
private:
point end_point_1, end_point_2;
//...
};
int main (void)
{
std::vector<shape *> shapes;
// どうにかして shape の vector を埋める。
for (vector<shape *>::iterator iter (shapes.begin());
iter != shapes.end();
++iter)
{
iter->draw();
}
}
全てのインタフェースクラスは仮想デストラクタを持つべきである。 仮想デストラクタにより、shape が多態的に delete されたときに、派生クラスの正しいデストラクタが呼び出されることが保証される。 さもなくば、リソースリーク(解放漏れ)の好機となる。 インタフェースクラスを使って設計を表現することによる利益はたくさんある。
- 新しい shape の抽象を、shape インタフェースのみに依存しているコードを変更することなく追加できる。例えば、square を shape から継承して、独自の方法でインタフェースのメソッドを実装することができる。main() 関数は全く変更する必要がない。
- 実装からインタフェースを分離することで、インタフェースクラスにのみ依存しているプログラムの部分を再コンパイルしなくてすむ。
- 実装クラスは互いに依存してはならないと依存関係逆転の原則(Dependency Inversion Principle(DIP))は主張する。その代わり、インタフェースクラスによって表現される共通の抽象に依存するべきである。DIP はオブジェクト指向システムの結合(coupling)を削減する。
既知の利用
[編集]ほとんど全ての良い C++ オブジェクト指向ソフトウェア