プログラムの可読性に関する検討・名前

プログラムの可読性に関する検討 - Life like a clown の続き.今回は,「名前」についてです.

一貫した命名規則を使用する

名前は最も主観に依存する部分が大きく,しばしば宗教論争の火種にもなるので,深追いは危険な領域です.そのため,名前について最も重要な事は「(単一プロジェクト内で)一貫した命名規則を使用する事」となります.

名前のスペリング自体について言えば,nspending か numPending か num_pending かは趣味の問題だ.まともな慣習に一貫してしたがうことに比べれば,具体的なルール自体の重要性ははるかに小さい.

Amazon CAPTCHA

名前に関して言うと,具体的なルール自体(キャメルケースかスネークケースかなど)の間には各人の主観以上の優劣はない事がほとんどです.したがって,他の領域についてはともかく,「名前の付け方」に関しては一度決めてしまった規則についてはあまり後から口を挟まずに従ってしまった方が良いと言えます.

命名規則を細かく決めすぎない

一般的には,細かく規則を決める(俗にいう,コーディング規則でガチガチに固めてしまう)につれて,プロジェクト内のコードの統一感が増し可読性が向上すると言われます.しかし,命名規則に関しては,細かな規則を決めそれに(盲目的に)従ってしまった結果,人には読みづらい暗号のような名前になってしまったと言う事例がしばしば見られます.

これについての有名な事例に,Microsoft の(システム)ハンガリアン記法があります(参考:ハンガリアン記法 - Wikipedia).例えば,Microsoft Office Word 97-2007 Binary File Format Specification では,b, c, cp, dxa, dxp, dya, dyp, f, fc, grp, grpf, i, l, rg, w, xa, ya と 17 種類ものプレフィックスが定義されています(実際の変数名を見る限り,書かれてないだけでもっとあるはず).実際に変数名を決定する際には,これらを複合的に使用するため,mpibrcftsCellSpacingDefault や lcbSttbfffn のような解読不可能な変数が数多く並ぶ事になります.

これは極端な例としても,命名規則を細かく決めすぎてしまったため,名前が暗号のようなものになってしまったと言う事はしばしば起こり得ます.命名と言うのは複雑で完全な命名規則を作ることは非常に難しいため,ある程度ざっくりした命名規則で留めておくのが無難だろうと思われます.

よく使用される命名規則は以下のような感じになります.

  • 定数,グローバル変数,クラスメンバ変数,一時変数で区別できるようにする
    (例:CONSTANT, Global, m_member, temp)
  • 名前空間,クラス名,メソッド名で区別できるようにする
    (例:Namespace, Class, methodFoo)

単語の省略形の扱いに注意する

単語の省略形の扱いは意見の分かれる領域です.C のように割と省略形を使用する事に寛容な文化もあれば,Java のように「単語の省略は悪である」とする(?)文化も存在します.

単語の省略形を許容する側の論拠は,主にモニター(が1画面で表示できる)範囲にあります.行数と1行の文字数については独立してそのうちまた記述しますが,「可読性」の指標の一つに「1関数が1画面に収まるかどうか」と言うものがあります.この画面の問題から考えると,「クラス名.メソッド名(引数)」が100〜150文字程度で終わらない場合にはどこかで改行を挟む事になり,縦長になりがちになります.これを回避するために,場合によっては単語の省略形を使用すると言う事を検討します.

一方,単語の省略形を許さない側の論拠は,主に「最悪の状況の回避」にあります.これは,「単語の省略形を許してしまうと,書いた本人にしか理解できないような省略形を使ってしまう」と言う懸念から来ています.例えば,Office Open XML File Formats では(また Microsoft ですがw),以下のような XML 例が掲載されています.

<pic:pic>
    <pic:nvPicPr>
        <pic:cNvPr id="4" name="lake.JPG" descr="Picture of a Lake" />
        <pic:cNvPicPr>
            <a:picLocks noChangeAspect="1"/>
        </pic:cNvPicPr>
        <pic:nvPr/>
    </pic:nvPicPr>
    <pic:blipFill></pic:blipFill>
    <pic:spPr></pic:spPr>
</pic:pic>

この中に出現する省略形のいくつかを取り上げると,nvPicPr (Non-Visual Picture Properties),cNvPr (Non-Visual Drawing Properties),cNvPicPr (Non-Visual Picture Drawing Properties),spPr (ShapeShape Properties) と,リファレンス無しでは名前の意味すら理解できない状況になっています.ちなみに sp は Shape ですが,spc は Space の省略形だそうで,この辺りがさらにややこしくしています.この例からは,「似たような省略形を多用しない」と言う教訓を得る事ができます.

省略形を規則でどう縛るかは難しく,個人的には以下の 2通り程度になるように思います.

  • 単語の省略形をある程度許し,意味の分からない変数名についてはコードレビュー時に潰す
  • 単語の省略形を完全に禁止し,名前の冗長さから来る若干の読みにくさは許容する

省略形の扱いに関しては,IBMJava 命名規則が割りとシンプルで良いように感じたので,最後に紹介しておきます.

省略語は、控え目に、またよく考えて使用します

つまり、短縮形 (省略語) のリストをつくり、それらを賢く選択し、一貫性をもって使用する必要があります。たとえば、number という語の短縮形を使用したい場合は、nbr、no、num などの中から 1 つを選び (どれでもかまいません) 、選んだ短縮形を文書化し、その省略形だけを使うようにします。

長い名前は避けます (15 文字以内にするのをお勧めします)

PhysicalOrVirtualProductOrService というクラス名は、作ったときはぴったりのクラス名のように思えたかもしれませんが (もちろん、この例では、わざと長くしています)、この名前では長すぎるので、Offering のようなもっと短い名前への変更を検討する必要があります。

Java 命名規則

関数の命名規則に注意する

関数名は能動的な動詞を基本にし,とくに問題がなければそのあとに名詞をつけるようにしよう.

now = date.getTime();
putchar('\n');

ブール(真偽)値を返す関数の場合は,戻り値が不明確にならないような名前をつける必要がある.

if (checkoctal(c)) ...

この例だとどの値が真になってどの値が偽になるかわからないが,次の例なら

if (isoctal(c))...

引数が8進数だったらこの関数が真を返し,そうでなければ偽を返すことがはっきりする.

Amazon CAPTCHA

原則として,関数名(メソッド名)は能動的な動詞をつける事になります.ただし,setter/getter に関しては,hoge()/hoge(x) (もう少し言語自体に補助的な機能がある場合,hoge/hoge= などの場合もある)のように動詞を省略してメンバ変数名だけにすると言う慣習もあります.

関数名については,引数名の扱いで意見が分かれる事があります.

  • 関数名のみで機能の全てを表現する
  • 関数名,および引数名の複合で機能を表現する

例えば,あるクラス Foo のあるメンバ変数 m_bar に情報をセットするメソッドを定義する場合,以下の 2通りが考えられます.

void Foo::setBar(Bar bar) {
    m_bar = bar;
}

void Foo::set(Bar bar) {
    m_bar = bar;
}

前者は関数名と引数名が重複する事も多く,やや冗長な表現となります.一方で,後者は関数オーバーロード機能を持たない言語では実現が難しいなどの問題があります.これに関しても,どちらを使うにせよ一貫した方法で使用する事が望ましいと言えます.