プログラムの書き方(入出力処理とそうでない処理を分ける)

コンピュータ・ソフトウェアについてのことですが。

何が見やすいか、わかりやすいか、エレガントであるか、全体の把握がしやすいか、といったことについてはあちこちで議論されているからここではとりあえず問題にしません。

本題で述べるのは「ある切り口で対象を見ると何が見えてくるか?」それを理解するためのひとつの例です。

たとえばシェルプログラムやバッチファイルというものを書く場合は、あまり入出力のエラーを想定した書き方をしないと思います。(する場合あるし、こだわる人もいますが)これは何故なのでしょうか?

シェルプログラムで行えることは標準入力、デバイス、ファイルなどからデータを読み込み、処理し、最終的に標準出力、デバイス、ファイルなどにデータを書き出すことです。これらはプログラムの実行中は安定的に存在し、不測の事態を気にする必要はまずありません。入出力が持つ特性を知り、あとはシェルプログラムの内部での処理方法を理解すればプログラムが書けると言うわけです。慣れてくると入出力は空気みたいなものであって当然というような感覚が育ち、内部での処理方法のみに注目した思考のもとにプログラムを書いていけるようになります。

これが RubyJava や C のようなもうちょっと複雑なことができる言語になると、もちろんシェルプログラムのような使い方もできるのですが、比較的高度なデータ構造やアルゴリズムの操作が楽にこなせることもあってそういうものが見えにくくなってきます。

さてここでひとつの提案があります。このように考えてみてはどうでしょうか。

「入出力の処理とそうでない処理を分ける」

I/Oがあるならそこは分離し、その他はメモリ上でのデータいじりに専念するように分けましょう、ということです。ここでいうI/Oというのは別にファイル/ソケット/ポートなどに限ったことではなく、グローバル変数やメモリマップト領域へのアクセスも含みます。

ただこれは、クラスや関数ごとに役割を決めてしまえとかそういうことを言っているわけではありません。そうしたほうが都合が良いこともありますが、ひとまず処理の塊として入出力とそうでないところを分けて考えてみよう、と言っているわけです。

このような切り口でものを考えると、以下のような利点が得られます。

  • 外部からもたらされるデータを検査し、必要であれば排除するといったことがやりやすい
  • メモリ操作のみで成り立っている部分が安定化し、見通しがよくなる


これによって自動テストの導入がしやすくなり、共有変数などに対する同期の考慮もしやすくなります。

このあたりのことは「自動テストをしやすいコードを書け」とか「慣れだ場数だ」と言われるばかりで具体的にはどうするかというのを見かけないのでひとつのテクニックとして挙げてみました。こういう視点で既存のフレームワークを見て見るのも面白いかもしれません。

ただし、シェルスクリプトPHPCGIみたいな、そもそもが入力ありきの出力当然みたいなプログラムを書くことがとても多い場合は、割り切って「短く簡潔に書く」ことの方が重要だと思います。はい。