コメントが必要ないコードを書くよう努力しよう

前回のエントリは、このエントリを書くための、実は前振り。
不必要なコメントが有るより、必要なコメントが無い方が問題だと思うので、エントリを別にして、先に書いておくことにした。短いので、ぜひ先に読んでおいてほしい。





読んだ?


さて、前回の要点は、以下の三つ。

  • コメントは、コードではないというところが最大の欠点。
  • 不要なコメントは、ただ不要というそれだけの理由で、書いてはならない。
  • 文書化されておらず、コードで表現し切れていないことは、すべて書こう。

だから、できるだけ表現豊かにコードを書こう、というのが今回の要旨。
そうすれば必要なコメントは減り、したがってコメントとコードの乖離の危険も減る。

それがなんであるかを示す名前をつけよう



#include

double calc( double x )
{
return pow( x, 2 ) * sqrt( 3 ) / 4;
}

とか書くなと。何をしてるか分かっても、これで何が得られるのか、分からないから。



#include

double getRegularTriangleArea( double sideLength )
{
return pow( sideLength, 2 ) * sqrt( 3 ) / 4;
}

こう書いておけば、引数を一辺の長さとした正三角形の面積が得られることが分かる。


ヘンに省略しない。「type」だの「mode」だの「status」だのって、なんのことか分かりませんから。
長い識別子を作っても、実行速度にも使用メモリにも影響しないので、全然かまわない。
識別子を使うときは、エディタの補完機能*1を使おう。宣言時にtypoしてもコンパイルエラーにならないので、気づかずに使い続けてしまうけど。


命名の際には、正しい英語を使う。
ウィンドウを開くなら、「windowOpen」ではなくて「openWindow」。動詞が先、目的語が後。
英語が間違っていると、意味が伝わらない。


ちなみに、Cランタイムを使わないで、C++のみで書くと以下の通り。



#include

template< typename ArgType >
ArgType getRegularTriangleArea( const ArgType & sideLength )
{
return std::pow( sideLength, ArgType( 2 ) ) * std::sqrt( ArgType( 3 ) ) / ArgType( 4 );
}

Cランタイムは、いろいろと脆弱性を抱えているので、C++コンパイラを使っているなら、可能な限りC++を使おう。
まあ、VC8のソースを見る限り、pow()とかsqrt()は、組み込み型を渡すとCランタイムを呼ぶんだけど。

処理に名前をつけよう

なっがーい関数の中に、「ここでナニをやります」なんてコメントを見ると、「じゃあ関数に切り出しておけよ」と思う。
「処理に名前をつける」とはそういうこと。どんな名前をつけるかは、上記参照。
関数名で処理の説明をしておけば、コメントの必要が無くなる。
if文の中で、複雑な判定式を書くのなら、その式を「isVeryComplicationCondition()」みたいな名前の関数に切り出して、判定結果をreturn文に渡そう。

リテラル値を使うのはやめよう

「3.1415926535」とか「"Input file name.\n"」とかを、直接式の中に書くのはやめよう。
円周率あたりはまだ分かりやすいが、



int diskType = 1; // HDDで初期化
...
switch( diskType )
{
case 0: // FD
...
break;
case 1: // HDD
...
break;
case 2: // CD
...
break;
default:
...
break;
}

なんてのは、まったく最悪。値を使う度にコメントを書かねばならないし、ひどいとそのコメントすらない。
こういうのは、enumを使おう。



enum DISK_TYPE
{
DISK_TYPE_FD,
DISK_TYPE_HDD,
DISK_TYPE_CD,
};

DISK_TYPE discType = DISK_TYPE_HDD;
...
switch( discType )
{
case DISK_TYPE_FD:
...
break;
case DISK_TYPE_HDD:
...
break;
case DISK_TYPE_CD:
...
break;
default:
...
break;
}

リテラル値に名前をつける際に、決して#defineを使ってはならない。constを使おう。
#defineは、コンパイルの前にテキストを置換するので、C++名前空間が食い破られてしまう。その傍若無人ぶりには、頭を抱える。
例えば、VCではmaxという#defineマクロが定義されているが、おかげでstd::numeric_limits::max()を使うことができない。
しょうがないので、こんなコードを書くのだ。



#include

#if defined(max)
#define RESERVED_MAX_MACRO max
#undef max
#endif // defined(max)
template< typename ResultType >
ResultType getMax()
{
return std::numeric_limits< ResultType >::max();
}
#if defined(RESERVED_MAX_MACRO)
#define max RESERVED_MAX_MACRO
#undef RESERVED_MAX_MACRO
#endif // defined(RESERVED_MAX_MACRO)

*1:VisualStudioならIntelliSense。アレは便利。VS2005になって、さらに便利。通常、テキスト編集はViViだが、コーディングの時はIDEを使うようになってしまった。