2019年11月17日日曜日

教科書に載らないソフトウェア開発入門 (インターフェースの定義と実装の詳細)

インターフェースの定義と実装の詳細

「教科書に載らない」と書くからには、教科書に書かれないような(でも非常に大切なこと)ことを書こうと思う。

働き始めて直ぐに横にいた先輩は、とある業務用ビデオ装置のディスク制御ファームウェアを書いていた人だった。この先輩は日本語の使い方しかり、話し方しかり、色々と事細かに注文を付けてきた。入社したての自分にとっては煩いだけの先輩だったのだけど、今思うと非常に大切なことをここで学んだように思う。

このシリーズの冒頭では、同じことを指示しているような一つの単語があったとしても、見方や認識の差から結果的に全く異なる場所に行きついてしまう点について書いた。実はこの先輩も同じような事を言いたくて若い頃の自分に事細かに指導をしてくれていたのだと思う。

ここ約20年ほど、様々なソフトウェア開発者の設計や実装を見てきて、如実に実力の差が明らかになるのは、インターフェースの定義と実装の詳細についての明確な思想が育っているか、という点だ。経験の浅いソフトウェア開発者、あるいは経験はあるはずなのに一向に技術レベルが向上しないソフトウェア開発者には共通して「インターフェースの定義と実装の詳細を明確に区別できていない」という問題があった。

おそらくこの区別を最初に身に付けておくだけでも、相当なレベルの差になりそうだから、ここで非常に重要なこととして「インターフェースの定義と実装の詳細」について述べておくことにしたい。


もしかしたら、とても重要でかつ唯一といって良いかもしれない。そしたらこのシリーズは既に今日の内容で終わってしまうことになるが、それはそれで良いだろう。

インターフェースの定義

インターフェースとは、ある面と別のまたある面の境界にある出入口のことだ。ざっくり言うとね。その出入口にはそれぞれ定められた規則があって、その規則に従う限り何かを通すことができる。”何か”っていうのがポイントだ。この通すものや規則を具体的に定めるのがインターフェースの定義。この定義に従う限り面と面を接続できる。

これはソフトウェアの言語に依らない考え方ではあるのだけど、具体的な例が欲しいのでここではC言語で書いてみようと思う。

int getc(FILE *stream);

例えば、上記の関数があったとしよう。この関数はFILEへのポインタを第一引数に取り、intを返値に持つ。この関数の実際の動作はインターフェースを定義した際に具体的な挙動が決められることになる。この関数はlibcの標準ライブラリだが、実際にこの一見簡単に見えるインターフェースでさえ、インターフェースの定義が明確になっていない限り、どのような動作になるのか見当もつかないはずだ。

例に挙げると
  • streamにnullを与えるとどうなるの?
  • streamに与えるFILEへのポインタとは一体なんなの?
  • streamのFILEへのポインタは開かれていないFILEだったらどうなるの?
  • 返値のintはどういう規則で返されるものなの?
のように細かな動作を決めるのはインターフェースの定義に含まれる。いずれにせよ重要なのは外部から見た出入口の振る舞いを決めるということだ。

このインターフェースの定義は何もソフトウェアに依らない。ハードウェアでも同じように考えることができる。このチップはI2CバスとSPIバスの橋渡しをするデバイスだ。インターフェースにI2CバスとSPIバスを取っている。


実装の詳細

実装の詳細とは、インターフェースの定義で定められた振る舞いを実現するためのものだ。とても乱暴に言うと、インターフェースの定義で定められた振る舞いさえ提供できれば、中身の実装がどんな風になっていても構わない。もちろん実装詳細の具体的な内容は時と場合によって正解もあり間違いも当然ながら存在するが。

例えば、二つの数を与えると加算して返す関数があったとする。インターフェースの裏側にある実装の詳細がコンピュータで実現されていても良いし、小人さんが10人で寄ってたかって一生懸命計算していても良いし、紙テープで何か不思議な計算処理をしていても良い。つまり、インターフェースの定義に従っていさえすれば良い、というのが実装の詳細の基本的な考えだ。

ここまでで

ここまででインターフェースの定義と実装の詳細について簡単に説明した。具体的なイメージは上記の説明だけでは不十分で、これから先のシリーズでより具体的な設計や実装のイメージをつかめるようにしていきたいと思う。

0 件のコメント:

コメントを投稿