Entry

プログラミングメモ - OOP の説明用サンプルプログラム

2009年01月03日

このサイトに掲載するつもりで,今オブジェクト指向プログラミング(OOP)の説明を書いているんですけれど,これってサンプルプログラムを考えるのが大変。この手のプログラムは,実用第一だと思うので,猫がにゃーと鳴くようなサンプルは,あまり役に立ちません。んなもんで,なんとかそれっぽく使えるサンプルをいくつか考えています。

一方,OOP の説明をするにあたって,いくつか書籍を読んでみたんですけど,大抵の書籍では,いきなり「クラスとは」とかいった話が出てきてます。けど,いきなりクラスの話を持ち出すのは,ちと順序が違うと思うんです。こゆ説明の仕方だと,どうしても「辞書的な意味でのオブジェクト→現実にあるモノ→クラス」とかいった,わけの分からない連想にならざるをえない。たしかに OOP を説明する上で,クラスの概念は超重要だけれども,クラスを使うために OOP があるっつのは本末転倒です。OOP で作る手段として,クラスの概念があるわけで,目的と手段がひっくり返ってます。

つまり,OOP の初めは,クラスの説明よりも前に,「OOP で作ると何がうれしいのか」というところからはじめなくちゃいけない。この点,OOP のバイブル的な書籍である『オブジェクト指向入門 第2版 原則・コンセプト』では,ちゃんと順番を踏まえて書いている。

オブジェクト指向入門 第2版 原則・コンセプト (IT Architect’Archive クラシックモダン・コンピューティング)
バートランド・メイヤー
翔泳社
売り上げランキング: 35864
おすすめ度の平均: 5.0
5 本当のオブジェクト指向
5 内容の濃い好著
5 オブジェクト指向で迷ったら、この本
5 オブジェクト指向の基礎からみっちり学べる

「プログラムモジュール」といった,古典的なソフトウェアエンジニアリングの発想をキーワードにして,クラス概念の説明を始める書籍は,本書くらいなんじゃないかと思います。また,OOP を考える上では,「疎結合」とか「凝集度」といった概念が出てくるんですけれど,これもソフトウェア工学上の「モジュール性(modularity)」を議論するときに使われる概念で,OOP に特有の概念ではない。この点についても,本書は丁寧に検討を加えています。

結局のところ,OOP を説明する上で,プログラムモジュールの概念を抜きにして語ることはできないと思うわけです。ただ,本書は,少し学的な要素が強いもんで,実践的でないのがちと難です。個人的には,本書と実際の設計/実装を補間するような説明を書きたいところ。

で,サンプルプログラムなんですけど,クラスを使わずにモジュール性を意識したプログラムとして,こんなのを作ってみました。可変長のバッファを使って,ファイルから文字列を読み取るプログラムです。以前,似たようなのを書いたんですけれど(参照:qune: プログラミングメモ - ファイルから1行読み取る関数(続き)),こちらは外部/内部リンケージでモジュール化した版です(C++ だけれど,クラスを使ってないことを強調するために C っぽく書いています)。

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sample.h"

// モジュールの定数(非公開)
static const size_t BUFSIZE = 128;

// モジュール内で使う変数(非公開)
static bool alloced_ = false;
static size_t bufsize_ = BUFSIZE;
static char* buffer_ = NULL;

// モジュール内で使う関数(非公開)
static void clearBuffer_();

// モジュールを初期化する
int
initTextReader() {
    bufsize_ = BUFSIZE;
    clearBuffer_();

    buffer_ = static_cast<char*>(::malloc(bufsize_));
    if (buffer_ != NULL) {
        alloced_ = true;
    }
    return alloced_ ? -1 : 0;
}

// モジュールを片付ける
void
closeTextReader() {
    clearBuffer_();
}

// ファイルからテキストを読む
char*
textRead(FILE* fp) {
    assert(alloced_);
    assert(fp != NULL);

    char ch;
    size_t size;
    for (size = 0; (ch = fgetc(fp)) != EOF; size++) {
        // バッファサイズを越える場合は2倍のバッファを再確保する
        if (size >= bufsize_) {
            bufsize_ *= 2;
            char* tmp = static_cast<char*>(::malloc(bufsize_));
            if (tmp == NULL) {
                alloced_ = false;
                ::free(buffer_);
                buffer_ = NULL;
                break;
            }
            ::strncpy(tmp, buffer_, size);
            ::free(buffer_);
            buffer_ = tmp;
        }
        buffer_[size] = ch;
    }
    if (ch == EOF) {
        buffer_[size] = '\0';
    }

    return buffer_;
}

// 内部のバッファをクリアする(非公開)
static void
clearBuffer_() {
    if (alloced_) {
        ::free(buffer_);
        buffer_ = NULL;
        alloced_ = false;
    }
}

ヘッダはこちら(sample.h とします)。

#ifndef __SAMPLE_H__INCLUDED__
#define __SAMPLE_H__INCLUDED__

// 公開しているインターフェイス
int initTextReader();
void closeTextReader();
char* textRead(FILE* fp);

#endif

モジュールはこんな風に使う。

#include <stdio.h>
#include <stdlib.h>
#include <iostream>

#include "sample.h"

int
main(int argc, char* argv[]) {
    if (argc < 2) {
        std::cerr << "arg err" << std::endl;
        ::exit(-1);
    }
    FILE* fp = ::fopen(argv[1], "r");

    initTextReader();
    char* str = textRead(fp);
    if (str != NULL) {
        std::cout << str << std::endl;
    }
    else {
        std::cerr << "read err" << std::endl;
    }
    ::fclose(fp);
    closeTextReader();

    return 0;
}

こういうプログラムは,スレッド周りや複数のインスタンスが必要になった場合にまともに対応できないので,普通作らないと思うんですけれど,プライベートなメンバを持つモジュールをクラスなしで表現するとなると,構文単位では無理。リンケージ単位で外部リンケージを持つ識別子と内部リンケージにとどまる識別子を表現するしかない気がします。反面,関数をまとめただけで「モジュール化だ」とか言うこともできる(し,実際そう説明している説明もある)んだけれども,それだと,状態を持つオブジェクトについて説明することができない。

んなもんで,とりあえず完全ではないものの,こうした中間的な状態は紹介しといた方がいいんじゃないかと思います。ちなみに,これをもう少し発展させると,ハンドラを使ったプログラムになるわけで,これはあちこちでよく使われる C のインターフェイスになります。つか,そういう風に説明するつもり。OOP を知ってる人なら,上のソースをクラスを使ったものに書き換えることもできるはず。

OOP って,もともと大規模なソフトウェアを作るために必要になったものなので,数個のクラスでゴニョゴニョやるようなサンプルだと,あまりありがたみが分からないんじゃないかと思ったりします。かといって説明用のサンプルを考えるとなると,規模はある程度限られてくる。難しいですね,ここら辺。

Trackback
Trackback URL:
Ads
About
Search This Site
Ads
Categories
Recent Entries
Log Archive
Syndicate This Site
Info.
クリエイティブ・コモンズ・ライセンス
Movable Type 3.36
Valid XHTML 1.1!
Valid CSS!
ブログタイムズ

© 2003-2012 AIAN