コーディング・スタイルについて

Team CHAO


コーディング・スタイルとは

コーディング・スタイルとは、プログラムを書くに当たってプログラムを読みやすくするために、字下げや空白のあけ方、変数などのネーミングなどに定める一定のルールのことです。コーディング・スタイルを定めておくことは、多人数でソフトウェアを開発したり、プログラムの保守、管理を簡単にするために欠かせないものです。

コーディング・スタイルを決めた方が良い理由として、以下の点が挙げられます。

  1. ソースコードは、保守、デバッグ、再利用などが必要であること
  2. 保守、再利用は必ずしもオリジナルのソースコードを書いた人が行うわけではない
  3. 自分で保守、再利用するとしても、ドキュメントがなければ時間がたてば忘れてしまう
  4. 可読性の高いソースコードを書くことによってプログラムの理解度を高めることができる
  5. 一定のスタイルに従うことでケアレスミスを防ぐ

見た目にきれいなプログラムが必ずしも再利用に優れていたり、効率的に動いたりするわけではありませんが、見やすいプログラムを書くことでプログラムの可読性を高め、つまらないミスを防ぐことができるのです。

レイアウト

良いレイアウトの目的

レイアウトの詳細に関する多くの議論は主観的な美学の観点でなされることが多いのですが、多くの道によって同じ目的に達することができることが多いです。そして、良いレイアウト方式とは、次のような基準を満たしている場合が多いのです。

コードの論理構造を正確に表現する

良いレイアウトの第一の目的はコードの論理構造を示すことです。これはコーディング・ルールの基本原理です。多くの場合、プログラマはインデント(字下げ)やその他の空白によって論理構造を示しています。

コードの論理構造の表現が一貫している

レイアウト方式によっては多くの例外があり、一貫してそのルールに従うことが困難です。良いスタイルほどレイアウトに関する表現方式が一定であり、ほとんどの場合を尽くしています。

読みやすさが改善される

論理的なインデントであってもコードを読みにくくするものには価値がありません。コンパイラが必要とする空白のみを使用するレイアウト方式は論理的かもしれませんが、大抵は読めないものです。良いレイアウト方式はコードを読みやすくするべきです。

変更に耐える

最善のレイアウト方式はコードの変更にも十分耐えうるものであるべきです。一行の変更が複数の変更に及ぶようであってはいけません。

字下げや空白文字のスタイルについては個人の好みによって様々ですが、普通は一段にタブ一つ分の字下げを行います。最近はエディタや開発環境が自動的にインデント(字下げ)を行ってくれますのであまり気にすることはありません。ただ、字下げを行うときのタブの文字数は4文字くらいにしておいた方がプログラムが見やすくなると思います。良いレイアウトとは、例えば次のようなものです。字下げによって論理的構造を示しています。



public void start() {

  if(timer == null) {

    timer = new Thread(this);

    timer.start();

  }

}

また、一行にあまりたくさんの文字を詰め込まないようにすることも大事なことです。JDKのソースコードでは、一行は80文字以内に収められているようです。エディタでプログラムを見るときに横スクロールしなくてもよいように適当なところで折り返した方が良いでしょう。

クラス、インターフェースの記述

クラスやインターフェースについては、



static public ...

static private ...

public ...

protected ...

private ...

public void foo () ...

public void foobar (Object bar) ...

のように、アクセス可能な部分から隠蔽されている部分へ、パラメータの少ないものから多いものへと順を追って記述していくと、クラス全体が把握しやすくなるようです。

ネーミング・ルール

なぜネーミングが大事なのか

ものの名前には意味があります。プログラムにおける変数名もそれと同じで、ひとつの変数には必ず何らかの働きがあるはずです。それが名前に端的に表されるように、ひとめでその変数名の役割が分かるようにそのものの目的に合った名前をつけることが必要なのです。次の例で見てみましょう。

このようなコードより、



X = X - XX;

XXX = Aretha + SalesTax(Aretha);

X = X + LateFee(X1, X) + XXX;

X = X + Interest(X1, X);

この方が分かりやすいはずです。変数名に実際の意味を持たせ、ひと目でわかるようにしています。



Balance = Blance - LastPayment;

MonthlyTotal = NewPurchases + SalesTax(NewPurchase);

Balance = Balance + LateFee(CustomerID, Balance) + MonthlyTotal;

Balance = Balance + Interest(CustomerID, Balance);

変数名は次に示す3種類の情報を含みます。

  1. 変数の内容(何を表しているか)
  2. 変数のデータ型(整数・実数など)
  3. プログラム構造における整数の位置。例えば変数を定義するモジュール名、あるいは変数がグローバルであることを示すプリフィクス(接頭辞)など。

変数を表すような名前が思い浮かばないときには、その変数がうまく概念化できていない(目的が明確ではない)という証拠ですから、本当にその変数が存在する必要があるのか、最初の設計からみなおしてみる必要があるかもしれません。ただし、単純で一時的なループなどに使用する変数の名前は一般的に i, j, kなどを使用し、次のようにします。




for (i=0; i<10; i++)

  Data[i] = 0;

ただ、その変数がループの外でも使用されるのであれば、i, j, k ではなく意味のある名前をつける必要があります。また、ループの長さが数行以上である場合や、ネスト(入れ子)になっているような場合は、i が何を意味しているのかなどは時間がたてば簡単に忘れてしまいますので、ループ変数により意味のある名前をつけることが必要です。一時的なローカル変数およびパラメータの命名規約については、「ローカル変数とパラメータの名前」で説明します。そのほかの変数についての命名規約については、「ハンガリアン規約について」で詳しく説明します。

クラス名の付け方

クラス型の名前は、内容を示す名詞または名詞句で、長すぎないようにし、各単語の先頭を大文字にします。

例:



public class StringTokenizer {

  ...

}

メソッド名のつけ方

メソッド名に良い名前がついていれば、それだけでメソッドの動作内容を知ることができます。次に挙げるのは、効果的なメソッド名のつけ方のガイドラインです。

メソッド名には、はっきりとした動詞とそれに続くオブジェクトをつける

メソッド名は動詞または動詞句で、最初の文字を小文字にし、その後ろに続く単語の最初の文字を大文字にします。また、メソッド名にはそのメソッドの働きを端的に表す名前をつけるべきです。例えば、"changeValue()"などのように、<動詞><名詞>という風に名前をつけると意味がわかりやすいです。

意味のない言葉や薄っぺらい言葉を避ける

言葉によってはどのような意味にも受け取れるものがあります。例えば、handleCalculation() や processInput()などという名前は、メソッドが何をするのかが分かりにくいです。分かることは、メソッドが何らかの計算をすることや何らかの入力を行うということだけです。また、メソッドの動作内容が曖昧であるためにメソッド名までが曖昧になってしまうということもあります。メソッド名が曖昧だということは目的が希薄であるということに起因し、結果として名前まで曖昧になってしまうわけです。こうした場合の最善策は、メソッドとこれに関連するメソッド全体をはっきりとした目的をもったメソッドとし、メソッドの内容を正確に表現する名前をつけて再構成することです。

メソッドが行うすべてのことを表現する

メソッド名には、すべての出力とすべての副作用が記述されていることが望ましいとされています。あまり長すぎる名前になってしまう場合には、ひとつのメソッドにたくさんの仕事を詰め込みすぎているという疑いがあります。

共通操作に規約を設ける

変数 V である属性を get および set するためのメソッド名は、getV およびsetV にします。たとえば、java.lang.Thread クラスのgetPriority メソッドやsetPriority メソッドはその例です。

何かの長さを返すメソッド名は、length にします。たとえば、java.lang.String クラス の length メソッドはその例です。

オブジェクトに関するブール型の条件 V をテストするメソッド名は、isV にします。たとえば、java.lang.Thread クラスの isInterrupted メソッドはその例です。

オブジェクトを特定の形式 F に変換するメソッド名は、toF にします。たとえば、java.lang.Object クラスの toString メソッドや、java.util.Date クラスの toLocaleString メソッドとtoGMTString メソッドはその例です。

変数名のつけ方

final ではない変数の名前は、最初の文字は小文字にし、その後ろに続く単語の最初の文字を大文字にします。また、クラススコープを持つメンバ変数は変数の始めに"m_"を付けて、メンバ変数であることが分かりやすくなるようにします。詳細については後の「ハンガリアン規約について」で触れることにします。

それぞれの変数は、ひとつの目的だけのために使用します。よくある間違いは、ひとつの変数を異なる場所で異なる目的に使用することです。このような”テンポラリ”変数を使用してひとつの変数を複数の目的のために使用するのは避けるべきです。

また、隠れた意味をもつ変数を避けることも重要です。例えば、「CustomerIDは顧客番号を持つが、それが-1の場合にはエラーを示す」、というような場合です。変数をふたつの仕事に広げることは、変数が常に一方の目的には合致しないことを意味します。この場合は、2種類の情報を格納するために二つの変数を使用することで、さらに明確になるはずです。

定数の名前

一般的に、定数の名前はすべて大文字の 1 つ以上の単語、頭字語、または略語を並べたものにし、各構成要素はアンダースコア (_) で区切ります。クラス型の final 変数も、習慣的に同じ形式にします。定数名は内容がわかるようにするのは普通の変数の場合と同様です。

定数に名前をつけるときには、定数そのものを示す名前ではなく、定数が表す抽象的実態を示す名前にします。例えば FIVE などという定数名は、その値が5.0を表しているかいないかに関わらず、好ましくありません。定数宣言のときに FIVE = 6.0 などというのは奇妙だからです。良い例では、MAX_CHANNELなどという名前は好ましい定数名です。

ローカル変数とパラメータの名前

ローカル変数とパラメータには、短くて意味のある名前を付けます。これらの名前は、多くの場合、いくつかの小文字で構成され、必ずしも英単語にはなりません。

1 文字のローカル変数名やパラメータ名は、使用しないようにします。ただし、一時的な変数、ループ変数、および特定の型の任意の値を保持する変数は例外です。従来から使用されている 1 文字の名前には、次のものがあります。

byte 型に対する b
char 型に対する c
double 型に対する d
Exception に対する e
float 型に対する f
int (整数)に対する i、j、および k
long 型に対する l
Object に対する o
String に対する s
なんらかの型の任意の値に対する v
Graphics型に対する g
Image型に対する img

ハンガリアン命名規約について

ハンガリアン命名規約は、変数とメソッド(関数)の命名に関する一連の詳細なガイドラインです。”ハンガリアン”という用語は、ひとつにはこの規約が外国語のように見えること、もうひとつにはこの規約の作成者であるCharles Simonyiがハンガリー出身であることから採用されています。

ハンガリアン規約による名前は3つの部分から構成されます。ベース型、ひとつ以上のプリフィックス、そして限定子です。

ベース型

ベース型とは、命名される変数のデータ型のことです。ハンガリアン名では、ひとつのベース型を含むことができます。特定のプログラムに適用するための、そしてプログラムで使用するための標準とするための、いくつかの略語です。

ハンガリアン規約によるベース型の例

ベース型 意味
n int
b ブール型
by バイト
ch キャラクタ
l long
s short
u unsigned
ul unsigned long
w, y 座標値
wn ウィンドウ
str String, CStirng
d double

プリフィックス

プリフィックスは変数の使用方法を記述するものです。プリフィックスは小文字で書かれ、変数名の前に置かれます。次の表は標準ハンガリアンプリフィックスの例です。

プリフィックス 意味
a 配列
c 個数
d 同一種類の変数の値の差
e 配列の要素
g_ グローバル変数
h ハンドル
i 配列へのインデックス
m_ メンバ変数
p ポインタ
f フラグ

プリフィックスは小文字で書かれ、変数名のベース型の前に置かれます。必要に応じてベース型と組み合わせて使用します。例えば、文字の配列なら、配列を表すプリフィックス"a"と文字(キャラクタ)を表すベース型"ch"を合わせて"ach"となります。xの個数を表すときに"cx"などと使うこともあります。

限定子

ハンガリアン名の最後の部分は限定子です。限定子は、ハンガリアン名を使用しなければ恐らく名前の全てになるような、説明的な部分です。これまでに説明した良い名前のつけ方に関する規則を限定子に適応したものです。

限定子はプリフィックスやベース型と組み合わせて使用するべきです。次に、ハンガリアン規約を使用した変数名をいくつか示します。

変数名 意味
achDelete 削除する文字の配列
ich 文字の配列に対するインデックス
ichMin 配列の文字のうち絶対先頭のインデックス
ichFirst 配列の文字のうち、ある操作をするための最初となる文字へのインデックス
cchInsert 挿入すべき文字の個数
pachInsert 挿入すべき文字の配列へのポインタ
m_nCurrentImage 現在の画像を持つクラスデータメンバの整数
g_lBalance グローバルレベルの残高を持つロング値

ハンガリアン命名規約の利点

ハンガリアン命名規約には、通常の命名規約の持つ利点以外の利点があります。それは、多くの名前が標準的に決められているため、個別のプログラムやルーチンで覚えるべき名前が少なくて済むことです。ハンガリアン規約は複数のプロジェクトに適応できるほど広範なのです。

また、ハンガリアン命名規約により、コンパイラが検査できないような抽象データ型を使用しているときに、厳密に型を検査できます。弱い型システムを持った言語や環境において、型をドキュメントする機能を果たし、名前をよりコンパクトにすることも出来ます。

まとめ

コーディングスタイルは人によって様々ですが、大切なことはあるひとつの決まりに従ってコードを書き続けることです。誰にでも分かりやすいプログラムを書けるということは理想ですが、それは大変難しいことです。せめて自分の書いたコードが半年後、一年後でも理解できるように、コーディングにルールを決めて、適切なコメントを付けるような習慣を身につけるように心がけるとプログラムの力も上達するはずです。


Editor : Kota Iguchi ( kota@sysportcore.com )