『手続き型』のプログラミング言語を学ぶポイント
手続き型言語というと、C言語だったり、もっと古いとCOBOL、FORTRANなどがあります。
古い言語っていう印象があるかと思いますが、オブジェクト指向言語もメソッドの中は基本的に手続き型です。
オブジェクト指向って手続きとデータをオブジェクトの一体として扱うものです。
Javaであれば
・手続き = メソッド
・データ = フィールド(インスタンス変数)
C++であれば
・手続き = メンバー関数
・データ = メンバー変数
となってます。
よって、メソッドやメンバー関数の中は手続きなので、手続き型言語のテクニックを学ぶとオブジェクト指向プログラミングにも大いに役立ちます。
そんなわけで今回は「手続き型のプログラミング言語を学ぶポイント」を紹介します。
専門学校で習った基礎中の基礎の大事な話
専門学校でプログラミングを習った時に、はじめに言われたことがあります。
「同じことを何度も繰り返し書かないようにしましょう = 重複したコードをなくしましょう」
同じコードがいくつかあったら、関数に切り出して共通化して、それを呼び出すようにしましょうということです。
これをやっていくと大抵の場合、うまくコードを書けると思います。
「わかりやすく書くために、長いコードは適切に関数・メソッドに切り分けていきましょう」というのもありますが、これって、人によって何行続いたら長いと感じるかって異なるので一概には言えませんが、「重複してるコード」というのは誰にとっても同じです。ですから、一つの確固たるポリシーになるわけです。
そして、もう一つのポイントは「構造化プログラミング」です。す。
構造化プログラミングをしよう!
構造化プログラミングを簡単に言うと「GOTO文を使わないようにしましょう」ってことです。Wikipediaを見ると、「構造化プログラミング = GOTO文を使わないようにする」という認識は誤りであると書かれていますが、ひとまず、理解するための最初の入口としてGOTO文を使わないというトピックから理解していくことは良いと思います。
C言語やC++ではGOTOという命令があります。GOTOは指定した場所に処理をジャンプさせる命令です。
START: if (user == "admin") { GOTO ADMIN_LOGIC; } ....何らかのコード ....何らかのコード ....何らかのコード ADMIN_LOGIC: ....何らかのコード ....何らかのコード ....何らかのコード if (status == "やり直し") { GOTO START; }
GOTO ADMIN_LOGIC;って書くと、ADMIN_LOGIC:と書いた場所に移ります。
その後、GOTO START;と書いたところでは、START:の地点に戻ります。
コードがあちこちに行ったり来たりします。しかも、そこにルールはありません。
普通プログラミング、if ~だったら、~をするみたく、制御構造が階層的で秩序がありますが、GOTO文を使うと、印をつけたところにいきなり飛んでいってしまうので、プログラム構造が無秩序になってしまうんです。
こういうコードって、書いた本人ならば、わかっても他人が読むとわけがわからなくなります。このサンプルだとコードが短いから、そこまで難しく感じないでしょうけど、これが何万行もあるコードの中で、GOTO XXXX;と数万行先にジャンプされたら追っていけないですよね?
ですから、最近のプログラミング言語にはGOTO文はありません。JavaにもPHPにもRubyにもGOTO分はないんです。
また、GOTO文を多用したコードをスパゲッティコードと言います。スパゲッティのように複雑に絡み合っているというのが名前の由来です。
名前は美味しそうなんですけど、決してそんなコードは書いてはいけません(笑)
プログラムの階層を意識しよう
大きなものって階層的に分けていくと、把握がしやすくなります。
何かを説明する時にも、大枠を説明してから、詳細を説明した方がわかりやすいですよね。
プログラムを書くときも同じで、大きな処理ごとに関数・メソッドを作って、それらを呼び出す、全体の流れを最初に作って、その後、各メソッドに詳細ロジックを書いていくと、読みやすく書きやすいコードになります。
その時に、階層化のレベル = メソッドに切り出す単位のレベルを合わせる必要があります。
例えば、あるプログラムでテキストファイルを一行ずつ読み込んで処理するコードがあったとします。そのループの中を関数・メソッドに切り出したとします。 その場合、別のプログラムでも、同じようにループの中は関数・メソッドに切り出します。すると、階層化のレベルが統一されたコードになります。
PHPでコード例を示します。
$input_file = fopen('uriage_data.txt', 'r'); while (($line == fgets($input_file)) !== false) { handle_uriage_one_data($line); } function handle_uriage_one_data($line) { … 一行を処理するコード … }
という風に実装したのに、別の同じようなプログラムで
$input_file = fopen('user_data.txt', 'r'); while (($line == fgets($input_file)) !== false) { … 一行を処理するコード … }
と実装したら階層のレベルが合っていません。前者は一行の処理を関数に切り出しているのに後者はループの中に直接をロジックを書いているからです。
そうではなく後者を
$input_file = fopen('user_data.txt', 'r'); while (($line == fgets($input_file)) !== false) { handle_user_one_data($line); } function handle_user_one_data($line) { … 一行を処理するコード … }
と書いた方が、コードの書き方が揃うので、理解がしやすいし、書く際にも自分の中で書き方をパターンか出来るので、発想やコードパーツを再利用しやすくなります。
チーム開発では他のメンバーに合わせるべき?
趣味などで自分一人で開発する場合は自分で階層化レベルのルールを決めればいいんですけど、会社でチーム開発をする場合は、自分の中だけで統一しても他の人とずれていたら意味がありません。
厳密にコーディング規約が決まってる会社もありますが、そうでない会社も多いものです。
そんな時どうすればいいかというと、自分のコーディングルールを、できるだけ一般的なものにします。
「こういう処理だったら、こういう風に階層にするのが一般的だよな!」っていう発想でコードを書くんです。
自分の個人的な意見ではなく「一般的にどうかな?」とか、「あの先輩だったら、どう書くかな?」などのように考えるんです。
そうやって作り出したルールでコードを書いていけば、大体は他のメンバーが書いたコードと同じような階層分けのコードになると思います。
こうすれば、厳密なコーディング規約がなくても、ある程度コードレベルの揃ったソフトウェアになるはずです。
とはいえ、自分が一般的と思っている書き方でも、他の人はそう思わないかもしれませんし、他のメンバーには一般的なコードを書こうというモチベーションがないかもしれません。
こういう場合、一番いいのは他のメンバーが書いたコードを読んでみることです。「この人はこういう処理ではこういうふうに書くのかぁ」とか、「このプロジェクトのコードはこういう特徴があるな」などというのが見えてきます。
そうして見つけた特徴に合わせてコードを書けばいいんです。もちろん、あまりに誤ったコードの書き方をしているのならば、それに従う必要はないですし、「これ、こういう風に変えたほうがいいと思います」という提案をすればいいでしょう。
プログラミング作法を啓蒙しよう!
「一般的なコードを書きましょう」という話は私が勝手に考えだしたものではなく、カーニハンとロブ・パイクが書いたプログラミング作法という名著でも述べられています。
この本の中で良いコードとは
- 簡潔性 …コードに重複がなく簡潔であること
- 明瞭性 …わかりやすいこと
- 一般性 …一般的な書き方であること(奇をてらったものでないこと)
とされています。この記事で紹介した内容とも合致していますよね。
あまりに汚いコードを書く人がいたら、読むようにぜひ勧めてみてください。
プログラミング作法は2000年に出版された本でやや内容が古いので、リーダブルコードも合わせて読むといいと思います。