21世紀に入って随分たちますが、残念なことにオブジェクト指向を使った場合のメリットは、依然として常識レベルの知識になっていないようです。Cに固執する人の中には、「頑張ればCで書けないことはない」といい張る人が多いようです。しかし、書けるかどうかということと、楽に書けるか否かということはまったく別の話です。
例えば以下に示すコードは、オブジェクト指向アプローチを取ったときに得られるメリットの典型例です(注)。
void Caller1() { CReqA* req = new CReqA( 1 ); // 要求の作成 Process( req ); // 要求を処理させる (以下略) }
呼び出し側1 |
void Caller2() { CReqB* req = new CReqB(1, 1);// 要求の作成 Process( req ); // 要求を処理させる (以下略) }
呼び出し側2 |
void Process( CReqBase* req ) { req->Exec(); // 渡された要求の実行 }
呼ばれた側 |
Caller1()、Caller2()ともに要求を作り、Process()という関数に引き渡しています。渡されたProcess()は、要求の実行を行います。「何が便利なのか」というクレームが出そうですが、よく見てください。Caller1()とCaller2()では確保している要求の型が違います。違うのに、Process()はその差を意識することなく実行を行っています。
あまり便利そうに思えませんか? ではこんな例はどうでしょう。
// どこかの関数の一部 CReqBase reqarray[100]; (reqarrayに対する要求の格納処理) // 要求の一括実行 for ( int i = 0; ( i < 100 ); i++ ) { if ( NULL != reqarray[ i ] ) { reqarray[ i ]->Exec(); // 要求の実行 } }
オブジェクト指向的なコード |
このとき、ポインタ配列reqarray[100]の内容が以下のようになっているとします。
要求の一括実行を行うfor文は、個別の要求の型を意識することなく処理を行っています(正確にはCReqBase*を処理しているつもりしかありません)。従来のコードであれば、for文の中で個別の要求の型を読み分け、キャストか何かを行って実行していたのではないでしょうか。例えば以下のように。
// どこかの関数の一部 CReqBase reqarray[100]; (reqarrayに対する要求の格納処理) // 要求の一括実行 for ( int i = 0; ( i < 100 ); i++ ) { if ( NULL != reqarray[ i ] ) { if ( reqarray[ i ] がCReqAのオブジェクトなら ) { (CReqA*)reqarray[ i ]->Exec(); // 要求の実行 } else if ( reqarray[ i ] がCReqBのオブジェクトなら ) { (以下略)
従来型のコードのイメージ |
この方式の素晴らしさは、reqarray[]がポイントするオブジェクトの種類が増えたときに発揮されます。従来型のコードのように、呼び出し側でオブジェクトの型を意識していると、要求の種類が増えたときに合わせて手直しをする必要があります。しかしオブジェクト指向的なコードの場合、呼び出し側のコードを一切触る必要がありません。どうでしょう、便利だと思いませんか。
一般にオブジェクト指向をサポートしていると主張する場合、以下の3つの概念をサポートする必要があります。
上記のコード例は、ポリモーフィズムのメリットを説明するものです。この考え方はSymbian OSの至るところで使われていて、文字列処理も非同期フレームワークもポリモーフィズムを利用して効率的な設計がされています。
また、構造体のフィールドが勝手に触られてしまうというCの問題点は、オブジェクト指向ではカプセル化により解決されています。
Copyright © ITmedia, Inc. All Rights Reserved.