プログラムのコメントについて、「正しく書かれたソースコードにコメントは必要ない」なんて幻想だという話および「正しく書かれたソースコードにコメントは必要ない」なんて幻想だという話(補足編) から議論が盛り上がってます。
個人的にはこのブログの記事に対して「そうだなー」って思うところなのですが、それに対するブックマークコメントを見てるとそうでもなさそうな意見も結構多く、少し驚いています。
実際にプログラム書くときはケースバイケースになるので擬似コードに熱くなると収拾つかないかもしれませんが、ちょっと思ったことを書いておきたい。
目次
コメントを書くか、関数にするか
ここで事例として取り上げられているコードは、以下のものになります。
まず、元のコードはこうです。
if (alpha < 1.0f){ //処理A }
で、それにコメントで補足したもの。
//フェードインが未完了の場合 if (alpha < 1.0f){ //処理A }
一方で、コメント使わずに関数化などをして、式自体に意味を持たせるようにしたもの。
関数化した場合はこちらで、
function isFadeinCompleted(){ return alpha < 1.0f } if (isFadeinCompleted()){ //処理A }
変数にして結果を保存したものはこちらです。
var isFadeinCompleted = alpha < 1.0f if (isFadeinCompleted){ //処理A }
(ブコメに貼ってある 「その事例だとコメントは不要だと思います」についても、ラムダ式になっているだけでやっている意味としては同じなので割愛)
それで率直に聞きたいんですがみんなマジで後者のほうが分かりやすいと思っています?
別の言い方をすると、初見でこのコードを見た人が理解しやすいのはどちらだと思います?
前者は「フェードアウト完了してないなー」で済むんですが、後者の場合はその関数の中身がちゃんと思った通りに動いているかを確認しにいって戻ってこなければならない。
昨今はIDEが発達してるのでそこまで手間ではないかもしれませんが、頻繁に出てきたら確実にイラッとします。
加えて、わざわざ関数や変数を作って置き換えるってのも、コメントの代わりに式書いてるだけやんって話でしかなくて、可読性にも処理コスト的にもプラスになっているとは思えません。
コードは短ければ短いほど良いのではなかったのか。(これもやり過ぎると可読性が死にますけど)
今回のケースだけでいうと、「alpha < 1.0fにコメントを付けないだけのために関数化する」ことが、プログラムを余計複雑にしている、本末転倒感があるので、そちらの意見にはあまり賛成できませんでした。
しかし、それはそれとしてコメントを書かないことを文化にしている話もあります。
このあたりはもう文化としかいえないところもあり、どちらが正しいかは答えのないものでしょう。上の記事の会社は、追記のところを見ると可読性を高めるための施策がだいぶ積まれているのでつよいと思います。(今回はリファクタリングの話なので、少し話がずれるかもしれませんが)
関数化することのデメリット
メソッド名が正しく名付けられていなければならない
関数化した場合に、どこでその動作について判断するか。それはその関数名、メソッド名が正しく動作を表すように名付けられていなければならない。
function isFadeinNoCompleted(){ return alpha < 1.0f } if (isFadeinNoCompleted()){ //処理A }
例えばこうなってたらどうでしょうか。まあバグなんですけど。バグりますよね。
//フェードインが完了している場合 if (alpha < 1.0f){ //処理A }
こっちとどっちが気づきやすいでしょうか。
関数名は処理を表している、だが処理を正しく表しているとは限らない。
フェードインが完了しているかを判定していると言ったな、あれは嘘だ。みたいなことはまれによくあります。
なので勿論毎回ではないにせよ、疑ってかかる必要は出てきます。可能ならその手間を増やしたくない。
呼び出しコストなどが余計にかかる
今のリッチな環境においてはおっさんの戯れ言でもあるので(環境によっては)別に無視してしまってもよいのですが。
関数化すると、当然そこに呼び出しのオーバーヘッドが発生します。
次のようなコードを書いて、UnityのUnitTestで実行してみました。if文で判定しているものと、そこだけを関数に分けたものを用意して、100万回実行してみます。
public class FunctionTest { private float alpha = 0.1f; [Test] public void Bentchmark() { long start, end; // 100万回繰り返して計測してみる start = DateTime.Now.Ticks; for (int i = 0; i < 1000000; i++) { // フェードインが未完了の場合 if (alpha < 1.0f) { // 処理A ; } } end = DateTime.Now.Ticks; // Ticksの単位が100ナノ秒なので、ミリ秒に変換する Debug.Log ("Time(if): " + (end - start)/10000+" ms"); // 100万回繰り返して計測してみる start = DateTime.Now.Ticks; for (int i = 0; i < 1000000; i++) { if (isFadeinCompleted()) { // 処理A ; } } end = DateTime.Now.Ticks; // Ticksの単位が100ナノ秒なので、ミリ秒に変換する Debug.Log ("Time(function): " + (end - start)/10000+" ms"); } public bool isFadeinCompleted() { return alpha < 1.0f; } }
それで結果は、if文が11ミリ秒、関数が22ミリ秒。倍の時間が掛かっています。
Time(if): 11 ms Time(fadein): 22 ms
といっても100万回回してこの程度の話でもあるので、ほとんどの場合は無視してもよいレベルの差異です。ただ、ここにオーバーヘッドが掛かることは覚えておくと、何かのときに助かるかもしれません。
原則として、やるか、やらないか
とはいえ、私も別に関数を可能な限り展開してコメントを書け、と言いたいわけではなく。実際に短い判定文でも抜き出して関数化することはあります。
ケースとしては、同じ判定が2回以上出てくる場合や、判定条件が複雑になっている場合などですね。
前者は、判定条件を一括で変える必要が出てきたときに、「if ( alpha < 1.0f )」が複数箇所にあると見落とす(=バグる)可能性が高まりますので、それを避けるため。後者は単純に、条件がごちゃごちゃ繋がっているより関数化して一つにしたほうが見やすいからです。
例示されているコードが単純なものなのでコメントでいいのでは、ってなっていますが、関数化する意見の方々は、それを変更する場合など、ここだけではない後々のケースまで考えているのかもしれません。
だったとしても、たぶんこう書いちゃうかなあ。
function isFadeinNoCompleted(){ return alpha < 1.0f } // フェードインが未完了の場合 if (isFadeinNoCompleted()){ //処理A }
コメントは意図を伝えるために書くべき
結局、どこでどうコメントを書くかについては、「意図を伝えられるかどうか」だと考えています。意味のあるコメントとないコメントの違いはここにあります。
そこは元記事でも触れられていて、
(上の条件に「alphaが1.0fより少ない場合」とコメントしてあったら「それは見ればわかるだろ!」と叫びたくなる)
とあり、これまた「ですよねー」って話です。
議論の流れとして「提示された擬似コードがアリかナシか」みたいになってしまっているところがあり、話が狭まってしまっている感じは否めないのですが。
そもそもの話として提示されていることは「コードが正しければコメントが不要など幻想」から、「コメントは意図を伝えるように書こう」だと思うので、やはりそこに全く異論はないですね。
そもそも「正しく書かれたソースコード」が条件の時点で前提が成り立たず幻想でしかないですから。
それからこれも主張しておきたいのですがプログラムの中に日本語のコメントがあると安心する。
僕が英文読めない雑魚なので、そういう現場で生きていけないことに異論はありませんけど。いくら関数名や変数名が正確に書かれてても、母国語でちょっと書かれた文のほうが読みやすいと思いますね。
そういう意味では、英語が母国語、ないし慣れている場合は「コード=コメント」説も納得できるかもしれません。
(でも日本語プログラミング言語ならコメント代わりになるかと言われれば違うと思うから、やっぱり別問題なのかも)
リーダブルな関数・定数とコメントは別にトレードオフじゃないんでは。コメントの存在意義は、コードが表現できないwhyを表現できること。このブログの例なら、コメントで表現すべきは「alpha<1.0はフェードイン未完了であること」より「フェードイン未完了なら処理Aしたい理由」だと思う https://t.co/dqqz7S64E6
— takasek (@takasek) 2017年5月21日
書かれているコメントの意義についての話をすると、「alpha<1.0がフェードイン未完了だけのコメント」は重要性が低いと思われるところもあるかもしれません。(だからいろいろ出てくるのかも)
どのレベルで説明するのが適当か、は主観的な話になってしまうので難しいですが、私としては「alpha < 1.0fはフェードインが未完了のとき」であることは、まあ別にコメントとしてはいいかなと思います。
「フェードイン未完了=alphaが1.0以下」であることがすぐ出てくるなら不要ですが、そのへんは人によるだろう(少なくとも私は直感的には出てこない)し。また判定したい条件そのものをコメントしておくのは、後からそのあたりの処理で判定条件が変わるときなどに役立つかもわかりませんし。
それはそれとして、「フェードイン未完了なら処理Aにしたい理由」もアリだと思います。つまり両方書けばいい。
と、ここまで書いてみて、やはり主観的な判断の話になるから難しいなとは思いました。
しかしコメントはどうせコンパイラがそぎ落としてくれるので、もりもり入ってて(処理速度的に)困ることはないはずです。
なのでどんどんコメントを書いて、意図を伝えていけると良いと思います。
読みやすいコードについては「リーダブルコード」がおすすめ
コメントについての話も含めて、「読みやすいコード」については「リーダブルコード」が超オススメです。
この記事の発端となったコメントについても、「意図を伝えるように」とのことが書いてあったりします。
関心のあるプログラマは是非ご一読を。
オライリー直販には電子書籍版もあります。
リーダブルコード ――より良いコードを書くためのシンプルで実践的なテクニック
手前味噌ですが別サイトで感想など書いてますので、ご参考まで。
「リーダブル・コード」感想 より読みやすく、分かりやすいコードを書くために
コメント書くか書かないかって話は不定期に出てくるものかもしれません。自分の感想を改めて見ると、(この記事がきっかけだったこともあり)コメント不要になっていくのかな、とコメントの必要性については弱気な感じもしますが、んーやはり「コメントは要らない」と言うためには前提条件がいろいろ積まれる感じですかね。