● gotoは悪.でもなぜ? 「C言語でgoto文はできるだけ使うな」という話を聞いたことがあるでしょうか? これは構造化プログラミングの話です.C言語では構造化プログラミングがサポートされており,基本的にプログラムの流れは上から下へ流れています.goto文は,このプログラムの流れを乱すことができる命令です.なので,できるだけ使わないようにしたほうが良いわけです.むやみに使うと,プログラムの流れが乱れてしまいます. しかし,goto文をまったく使うなということではありません.goto文を使って「プログラムの流れを乱すな」と言っているわけです.プログラムの流れが乱れると,プログラムをいくつかのブロックに区切って理解することが困難になるのです. 人間の頭はそれほど良くないということを先ほど書きましたが,「プログラムの流れを乱すな」というのも,これが原因です.人間の頭では気にしておける事柄が少なすぎるのです.なので,普通,一つの関数であろうともそのままでは理解できません.しかし,プログラミングをするときはそれを理解しなければなりません.時折,プログラミングをするときにも理解していないのではないかと思うようなプログラムを見ることがありますが,そのようなプログラムは当然,不具合の山です. じつは,人間は一つの関数でもそのままでは理解できないので,いくつかのブロックに区切って理解しているのです.つまり,関数をいくつかのブロックに区切り,その各ブロックを理解しているわけです.そこで今度は,理解したブロックを,詳細を考えずに一つのブロックと考えます.関数の中にブロックが多ければ,複数のブロックをまとめて一つのブロックとして理解します.それでようやく関数が理解できるのです. 一つのブロックは数行が良いのですが,簡単な規則を適用して作られているだけなら,たとえ長くてもその範囲を一つのブロックにすることができます. たとえば,次のような形なら100行を1ブロックとして考えることもできます. a0=0; ... a99=0; この場合,そのまま100行分を理解せず,「a[0〜99]=0;」として理解しているわけです.そのように表す,つまり次のような書き方のほうが理解しやすいわけです. for(i=0;i<100;++i){ ところがgoto文を使うと,ブロックとして一まとめにすることが困難になります.ブロックの途中に飛び込むと,それは一つのブロックになり得ません(ただし,パターン化しているのならば別).ブロックに分けられなければ,人間は一つの関数すら理解することができないのです. ● グローバル変数も悪.でもなぜ? 「グローバル変数を使うな」という話を聞いたことがあるでしょうか? goto文ほど有名ではありませんし,goto文ほど徹底されているわけでもないのですが,これもやはり「悪」です.しかも,goto文よりひどいことになります.これも人間の頭が(ある面)自分で思っているほど良くないせいです. プログラム全体を理解しようとしたとき,いくつかのブロックに分けることになります.基本的には関数で分かれているはずですが,プログラム全体ではいったいいくつの関数があるのか,数える気にもなりません.これをそのまま理解できるほど人間の頭は良くないのです.では,どうやって分ければよいのでしょうか? タスクで分ける,プロセスで分ける,モジュールで分ける…… それぞれの部署でいろいろな分け方をしていることでしょう.変数は,それを設定しているところと,読み出しているところを結ぶ働きがあります.つまり,プログラムの部分と部分をつないでしまうのです.これがグローバル変数ともなると,つないでいる可能性としてはプログラム全体で,関数一つに絞られるgoto文などとは比べものになりません. せっかくプログラムをブロックに分けたのに,グローバル変数はそれらを結んでしまいます.むやみにブロックとブロックを結んでしまうと,それらは強固に結びつき,結局一つのものになってしまいます.すると,プログラムをブロックに分けたことにならず,理解できないということになってしまうのです. プログラム全体を見わたすことができないということは深刻な問題です. とはいえ,まったくグローバル変数を使うなということでもありません.グローバル変数を使って「ブロックの結びつきを強めるな」ということです. ブロックとブロックをつなぐのはインターフェースの役目です.グローバル変数がインターフェースとして認識されていればグローバル変数も問題はないのですが,本当にそう認識しているでしょうか? goto文の場合,それを使わずに済ませることは可能ですが,そのために変数が増えてしまうなど,よりわかりにくくなってしまう例が多々あります.したがって,まったくgoto文を使わないという方針はおすすめできません.しかし,グローバル変数のほうは,それを使わずに済ませることが可能で,それによってわかりにくくなることはないので,まったく使わないという方針をおすすめします. 一つの方法として,グローバル変数をstatic変数にしてみることを考えましょう.その変数に書き込む部分と読み出す部分を切り出し,その変数と一緒に一つのファイルにしてしまうのです.こうすれば,とりあえずはグローバル変数ではなくなり,インターフェースは関数になるでしょう. かといって,単純なset/get関数はやめましょう.もう少し大きめに,その変数を使って何をしようとしているのかまでわかるような範囲でまとめてください.その変数が,たとえばリングバッファなら,「リングバッファに入れる」,「リングバッファから取り出す」くらいまでを切り出しましょう.「空かどうか調べる」,「いっぱいかどうか調べる」というのもあってよいでしょう. ● オブジェクト指向の前の話のまとめ 結局,「人間の頭はたいして良くないんだから,モジュールとして切り分けて考えよう」ということです.これは,オブジェクト指向以前からいわれていた話です.しかし,なかなかうまくモジュールとして切り分けることができないのが現実です. それでは指針は?というと,「グローバル変数を使わないこと」です.グローバル変数が,モジュールとして切り分けるのを阻害しているわけです.とはいえ,グローバル変数は結果や兆候であって,原因ではないのですが……. 「モジュールとして切り分ける」というのは,つまり「モジュールを定められたインターフェース以外では使わないこと」です.グローバル変数でインターフェースを取っているというならそれでも良いのですが,本当にそれはインターフェースとして定められたものだけなのでしょうか? グローバル変数を使っていて「モジュールとして切り分ける」ことがうまくいっていないのは,多くの場合,グローバル変数がしっかりとインターフェースとして定められていないためなのです. モジュールは定められたインターフェースだけで使いましょう.さもないと,それはモジュールとしては切り分けられていないので,人間の頭では理解できません.
Copyright 2002 石原 亘 |