🐕

「使い方」に着目したソフトウェアのモデリング

2023/03/20に公開

要約

これだけは覚えて行ってほしいのは、インタフェースとは「使い方」だということだ。

はじめに

多人数でのソフトウェア開発においては、複数の開発者に対して容易に変更可能なメンテナンス性を提供するコードを書くことが求められる。これを実現するために、オブジェクト指向を用いたドメインの分析とモデリングや、デザインパターンの実践が行われている。一方で、重要なオープンソースソフトウェアの多くが未だにCで記述されており、モデリングをオブジェクト指向と切り離して議論することには意義がある。モデリングに関する議論はオブジェクト指向のような方法論の提唱により行われてきたため、それらの方法論と切り離した議論は多くない。本稿では、モジュールというシンプルな概念を導入し、モジュールの使い方を定義することに着目して、ソフトウェアのモデリングを行う方法について論じる。

モジュールと抽象化

本稿では、モデリングについて述べるにあたって、モジュールという概念を抽象化の基本単位として導入する。

モジュール

プログラミング言語には通常、関数、クラス、ファイルといった、プログラム部品を作るための機能がある。また、プログラマは、まとまったプログラム部品を、任意にグループ化することができる。本稿ではこれらをモジュールと呼ぶこととする。モジュールは抽象化の単位であり、抽象化の結果として作成される。一つのアプリケーションの中には、複数の粒度のモジュールが存在する。モデリングとは、モジュールを適切に分割して階層構造や相互作用を決定することである。

インタフェース

モジュールには必ず使い方がある。これを本稿ではインタフェースと呼ぶ。インタフェースとはモジュールの使い方のことである。

実装

本稿で述べる実装とは、モジュールの機能を実現するコードや振る舞いのことである。

抽象化

本稿で述べる抽象化とは、モジュールにインタフェースを与えることである。つまり、使い方を定義することによってソフトウェアがモデリングされる。

データ型

整数型のようなプリミティブ型も、メモリというモジュールに対する言語仕様上のインタフェースとして捉えることが可能である。構造体も、メンバ名というインタフェースを与えられたモジュールである。

オブジェクト指向

本稿で導入したモジュールによる抽象化の概念で、次のオブジェクト指向の概念を説明可能である。

クラス

古くは、データ型が構造化されて構造体となり、プログラムが構造化されて関数になった。両者をさらに構造化したものがクラスであるが、これも本稿で述べるモジュールの一つである。オブジェクト指向言語はクラスの継承という、インタフェースと実装のコピー機能を持つが、これによる多態性、汎化、特化については後述する。オブジェクトを利用した際に呼び出される実装は、コンパイル時または実行時に束縛される。オブジェクトの取り扱いについては後述する。

隠蔽

一般に、モジュールを利用する側から見たときに、インタフェースさえ分かれば実装を知らなくても機能を利用できる。このことは、オブジェクト指向における隠蔽と対応する。

多態性

一般に、一つのインタフェースに対して、実装は複数存在できる。すなわちインタフェースに対する実装には多様性がある。オブジェクト指向の多態性はこれに含まれる。

汎化

異なるモジュールに同一のインタフェースを与えることは、オブジェクト指向におけるクラスの汎化と対応する。スーパークラスの作成はこれを言語機能としてサポートするものである。

特化

すでに存在するモジュールと同じインタフェースを持つモジュールを作成することは、オブジェクト指向におけるクラスの特化と対応する。サブクラスの作成はこれを言語機能としてサポートしたものである。

インスタンス

ここまでの説明だけではオブジェクト指向におけるオブジェクトのような実体化の概念をうまく説明できない。そこで、インスタンスという概念を追加する。本稿では、動的に確保されるメモリ領域をインスタンスと呼ぶこととする。オブジェクト指向におけるオブジェクトは概念としてはメモリ領域ではないが、実装としてはthisやselfなどはメモリ領域を示すとともに、そのメモリ領域はRTTIのようなクラス情報やvtableのような関数テーブルを参照するものであるからだ。

モジュールの階層構造

ここではMicrosoft Windowsを例に取る。Microsoft Windowsのシステムは大まかに、下から順に、HAL, NTカーネル, NTエグゼクティブ, Win32サブシステムの階層構造を取っている。各階層の呼び出し関係は基本的に上から下へ一方的である。

Microsoft Windowsは、HALを置き換えることで異なるプラットフォームへの移植を容易にしているほか、NTカーネルとNTエグゼクティブで責任を分界している。このように、モジュールを階層構造にすることで、移植性を高めたり、機能を積み上げるような構成にしたりすることが可能である。

モジュールの相互利用

あるモジュールが別なモジュールを一方的に利用する関係は、メンテナンスしやすいが、現実にはモジュール間で相互に利用し合う構造になることもある。

相互に利用し合うモジュール同士は、階層構造の直接の上下か、水平で隣り合わせの関係であることが、メンテナンスしやすい構造になる。なぜなら、あるモジュールを理解する上で知識が必要となる別なモジュールの数を減らせるからだ。

さらに、階層構造の下から上に向かう呼び出しは、コールバックのみとすることが望ましい。コールバックのインタフェースは下層側で定義されるから、実質的に上層側からの一方的な利用と言える。

モジュールとデータ型

オブジェクト指向におけるクラスであれば、モジュール自体がデータ型なので、特段意識する必要はないが、Cで開発するときに意識するとよいことがある。それは、モジュールが扱う構造体を一つに絞り、構造体のインスタンスのCRUDをモジュールで完結させることだ。例として、FILE構造体に対して、fopen, fread, fwrite, fcloseがある。

あるデータ構造とそれに対する操作をひとまとまりにしたものがオブジェクト指向におけるクラスであるが、モデリングの粒度という側面では、これは人間の感覚と相性がよいのかもしれない。

もちろん、インスタンスを生成する必要のないモジュールのために、構造体を作る必要はない。このあたりは、オブジェクト指向では***Managerなどの抽象度の低い名前のクラスを作ることに繋がりやすいが、このようなクラスはそもそもクラスである必要がないケースもある。

結論

本稿では、モジュールというシンプルな概念を導入し、モジュールの使い方を定義することに着目して、ソフトウェアのモデリングを行う方法について論じた。また、モジュールとオブジェクト指向の概念との対応を説明した。さらに、モジュールの階層構造や、モジュールが責任を持つインスタンスのCRUDについて述べた。

あとがき

この記事の元ネタは2005年、20才の頃に思いついたことのメモ書きで、長年実践してきた。どうやらそんなに間違っていないようだが、論文にするほど新規性はなく、書籍にするほど膨らむ話でもないので、ここで記事として公開することとした。本稿のエッセンスを、読者のソフトウェア開発に生かしていただければ幸甚である。

Discussion