良いプログラミング生活を!

良いコーディングのためのテクニック
2012年3月10日
株式会社フォーエス
作者不詳
はじめに
■対象とする人は、コーディングを実際に行う人全般。
■プログラミングではなくあえてコーディングと言っているのは、仕様や技術内容を
理解した上で純粋にコードに落とす段階のみを対象にしているため。
■プログラミング言語の例としてはJavaを使うが、ほとんどのテクニックはJavaだけで
なくほとんど全てのプログラミング言語で通用する。
■クラス分けの方法やライブラリの使用といった点については触れない。
■ネットワークやDBアクセスといった特定の分野に関する点についても触れない。
■個人的な見解が多く含まれているので、自分の考えとは合わないと感じる箇所は
無視してくれて構わない。
■ごく基本的で当たり前に思えることしか言うつもりはない。
2
良いコーディングとは・・・
私の考える良いコーディングとは、
シ
シンプルで
わ
わかりやすく
量
量産性の高い
コーディングのこと
3
シ わ
シンプル・イズ・ベスト
■シンプルなのは良いことだ。
■とにかくできる限りシンプルにする。
■ゴチャゴチャしていると、それだけわかりにくくなり、ミスも起きやすく
なる。
■パッと見ただけでは理解できないトリッキーなコーディングは一見凄
そうだが、技術不足もしくは只の自己満足にすぎない。
■真にできるプログラマーなら複雑な処理でも適切に分解して、シン
プルなコーディングの組み合わせで表現できる(はず)。
4
一つの箇所では一つの処理だけをやる
シ わ 量
■一つの箇所では一つの処理だけに専念して、複数の処理を混在さ
せない。
■わかりやすくなるのはもちろんのこと、再利用もしやすくなる。
■勘違いしてもらいたくないが、一つの機能毎にメソッドを分けろと
言っているわけではない。
むしろ一回しか使わない機能までいちいちメソッドに分けていては、
余計に複雑になるだけなのでよろしくない。
→続く
5
一つの箇所では一つの処理だけをやる
×悪い例
○良い例
Aの変数定義
Bの変数定義
Aの処理1
Aの処理2
Bの処理1
Cの処理
Bの処理2
Aの処理3
Aの変数定義
Aの処理1
Aの処理2
Aの処理3
Bの変数定義
Bの処理1
Bの処理2
Cの処理
A・B・Cの処理が混在している
シ わ 量
A・B・Cの処理が独立している
6
変数名やメソッド名はきちんと付ける
シ わ 量
■変数名にtotal2、count3のように意味のない数字を含む名前はなる
べくつけないこと。
■メソッド名にgetTotal2()といった意味のない数字を含む名前は絶対
につけてはいけない。
■英語名にする場合はスペルミスを避けるため、事前に正しいスペル
を調べてからつけること。
単に恥ずかしいだけでなく、使う側としても使いにくくなる。
■特別な名前をつける意味がないものにはあえて名前をつけず、汎
用的な名前(インデックスのiや、tmp、array、list、countなど)や一般
名(DTO, DAOなど)にしておくのは良い習慣といえる。
名前を考える手間を省けるだけでなく、量産性や、コードの断片レベ
ルでの再利用性につながる。
→続く
7
変数名やメソッド名はきちんと付ける
シ わ 量
■「to」の代わりに「2」、「for」の代わりに「4」を使ったりする人もいるが、
頭がどうかしてるとしか思えない。
この命名方法は、log4jといったメジャーなライブラリでも使われてい
るので余計に性質が悪いが、下らないので絶対にやめて欲しい。
■英語の頭文字を連ねた略称を使うのもやめた方がいい。
単純にわかりにくくなるというだけでなく、他の略称と重複していらぬ
誤解を招く必要がある。
■多少長くなっても良いので、誤解を招かないわかりやすい名称にし
ておいた方が良い。
今どきのエディタなら、フルで入力しなくても、途中まで入力してコー
ド補完機能を使えば、スペルミスなく素早く入力できる。
→続く
8
変数名やメソッド名はきちんと付ける
シ わ 量
×悪い例
○良い例
int xyzTotal = 0;
int total = 0;
XxxDao xxxDao = new XxxDao();
List<XyzDto> xyzDtoList =
xxxDao.getList();
XxxDao dao = new XxxDao();
List<XyzDto> list = dao.getList();
int count = list.size();
int xyzDtoListCount = xyzDtoList.size();
for(int i = 0; i < count; i++)
{
total += list.get(i).point;
}
for(int i = 0; i < xyzDtoListCount; i++)
{
xyzTotal += xyzDtoList.get(i).point;
}
意味のない箇所にまでいちいち専用の名前をつけると、使い回しの邪魔になるだけ
なので、シンプルな名前にしておいた方が良い
9
わ
とにかくコメントを書く
■全くコメントのない見知らぬソースを渡されたとき、あなたはどう思
いますか?
■コードはコンピュータに対してプログラムを理解させるためのものに
対して、コメントは人間に対してプログラムを理解させるためのもの。
■私にとってコメントを書くという行為は、プログラムに魂を込めること
に等しい。
■コメントを書けないということは、真にその処理を理解していないと
いうことを意味する。
→続く
10
わ
とにかくコメントを書く
★コメント不要論に対しての反論
■わかりきった処理にはコメントを書かない方がソースを見やすくなるという意見も
根強いが、私はそれには反対。
コメントを書くかどうかを選別する必要がでてくるし、今の自分には不要だと思って
も、未来の自分や他の人にとっても不要かどうかはわからない。
■思考ベースが英語の外国人なら、ソースそのものが英語の文章のように見える
ため、コメントは不要という意見が支配的なのかもしれないが、あいにく私達は
思考ベースが日本語なのでそうはいかない。
少なくとも私は、日本語でコメントを書いておいて、そちらを見る方がはるかに内容
を理解しやすい。
■「なんだかんだ理屈をつけても、結局はただコメントを書くのが面倒くさいから不
要だと言っているんだろ」と言いたい。
→続く
11
わ
とにかくコメントを書く
★コメントの効用
■まず第一に、ソースが格段にわかりやすくなる。
■客観的にわかりやすいコメントを書くことは、文章作成の能力アップにも役立つ。
■たとえコピペしてきたソースであっても、コメントを書いていくことで解析につながり、
理解が深まる。
■コメントが無いと、他の人が見た場合だけでなく、後で自分が見た場合も「なんで
こんなことしてるんだ?」と思って余計な修正を加えてしまい、バグの原因になっ
てしまうこともある。
→続く
12
わ
とにかくコメントを書く
★コメントの量について
■ソース本文を見なくても、コメントだけで処理内容が理解できるレベルまで書いて
おくのが理想。
もっと言うと、全ての行にコメントを書くのが最善。
■ JavaにおけるJavadocのように、コメントがそのままドキュメントとして出力される機
能を持つ言語の場合は、少なくとも出力されるレベルについては必ず書く。
■クラス・メソッド・変数については、それが何のためのものかという説明ぐらいは絶
対に書くこと。
「名前だけでわかるだろ」という意見もあるが、他人の命名のセンスなんか知った
ことでは無いので、コメントが無いとわかりやしない。
→続く
13
わ
とにかくコメントを書く
★コメントの内容について
■コメントにはなるべくコーディングの意図を書くようにする。
■一見わかりにくいことをしている箇所や、仕様上の特別な理由があってあえて特
殊なコーディングをする場合には、必ずコメントで理由を添えておく。
特にインデックスを0からではなく1から開始したり、ループを逆順に回すときは必
ず理由を書いておくこと。
■当たり前だが、ソースを修正したらコメントも一緒に修正すること。
ソースの内容とコメントの内容がずれていたら、混乱を招く。
■作りかけの箇所や、後で修正を加える予定の箇所には、「TODO:」、「FIXME:」と
いった目印になるコメントを仕込んでおくと良い。
→続く
14
わ
とにかくコメントを書く
★よろしくないコメント
■「2012/3/10 xxxのバグ修正 山田太郎」といった、変更履歴のコメントはやめること。
そんなものは、ソース中に書くのではなく、バージョン管理システムを使って管理し
た方がはるかにいい。
■同様に不要になったソースはコメントアウトしていつまでも残しておくのではなく、
バッサリ削除すること。
余計なものがいつまでも残っていては、単に目障りなだけでなく、読み違えたり、
誤って復活させてしまう可能性があるので、もはや有害なレベルに達している。
■単に「ループ」「メソッドの呼び出し」といったコメントは意味がないのでやめること。
「xxxに対してループをまわす」「xxx用のメソッドの呼び出し」といったコメントなら、
コメントだけで処理内容がはっきりする効果があるので大いに意味がある。
→続く
15
わ
とにかくコメントを書く
長々と書いてきたが、つまりは
コメントを書こう
の一言に尽きる
→続く
16
わ
とにかくコメントを書く
×悪い例
○良い例
//変数定義
int count = 0;
//件数
int count = 0;
//calcMaxPageメソッドを呼び出した結果を
変数maxPageに代入する
maxPage = calcMaxPage(count, 10);
//件数と1ページ当たりの表示件数(10)から
最大ページ数を算出する
maxPage = calcMaxPage(count, 10);
//ループをまわす
for(int i = 1; i <= maxPage; i++)
{...}
//最大ページ数にあわせてループをまわす
//(ページ番号は0からではなく1から始まる
ので、インデックスも1から開始する)
for(int i = 1; i <= maxPage; i++)
{...}
意味のないコメント
意味のあるコメント
17
コピペした箇所をそのままにしない
わ 量
■コピペすること自体はいいのだが、ただ単に貼りつけただけでその
ままにされていることがあまりにも多い。
■面倒だという気持ちはよくわかるが、きちんとコピー先にふさわしい
状態に手直しすること。
そうしないと、トラブルの元になって後々もっと面倒なことになりかね
ない。
■特にコメントや、具体名の入った変数名を放置しておくと後でわけ
のわからないことになる。
18
わ
空行をうまく使う
■見やすさを向上させるだけでなく、処理の区切りを表すことができる
◆Before
◆ After
Aの変数定義
Aの処理1
Aの処理2
Aの処理3
Bの変数定義
Bの処理1
Bの処理2
Cの処理
Aの変数定義
Aの処理1
Aの処理2
Aの処理3
Bの変数定義
Bの処理1
Bの処理2
Cの処理
19
区切りコメントを使って処理の区切り・階層化を表す
わ
■区切りコメントとはこんなの
//==========================================================
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
////////////////////////////////////////////////////////////
■毎回入力したり、コピペをしていると面倒な上に長さがまちまちに
なって美しくないので、IMEに他と衝突しない短い名前で単語登録
しておくのを推奨。
ちなみに私はこのように登録している。
ぬ
→ //==========================================================
ぬぬ → //_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
ぬぬぬ→ ////////////////////////////////////////////////////////////
→続く
20
区切りコメントを使って処理の区切り・階層化を表す
わ
◆Before
◆ After
for(int i = 0; i < 10; i++)
{
Aの変数定義
Aの処理1
Aの処理2
//=======================================
//AとB関連の処理を繰り返し行う
for(int i = 0; i < 10; i++)
{
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//A関連の処理
Aの変数定義
Aの処理1
Aの処理2
Bの変数定義
Bの処理1
}
Cの処理1
Cの処理2
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//B関連の処理
Bの変数定義
Bの処理1
}
//=======================================
//C関連の処理
Cの処理1
Cの処理2
21
常に使い回しを意識してコーディングを行う
量
■量産性を高めるためには、いかに使い回しをしやすくするかを考慮
することがとても重要。
■たとえ一点もののアプリだとしても、今後別のアプリを作るときに使
いまわせるコード断片は意外と多いので、決して無駄にはならない。
■ここでいう使い回しとは、クラスやメソッド単位の再利用だけでなく、
コード断片のコピペも含む。
コピペはコーディングにおいて悪者扱いされることが多いが、適切な
コピペなくしてコーディングの効率化はありえない。
22
シ
同じコードを何度も書かない
量
■同じコードが複数箇所に存在することは、単に無駄なだけでなく、
修正漏れの可能性も高くなるので大変危険。
■全く同じ処理が複数箇所に存在するときは、共通で使える箇所にメ
ソッドとして定義する。
メソッドの引数の差程度で吸収できる場合も同様。
■ややJava特有の話になるが、オーバーロードするときは、なるべく実
際の処理の実装は引数の最も多いパターンのメソッドに固めて、そ
の他のメソッドでは引数の多いメソッドを内部的に呼び出すように
する。
→続く
23
シ
同じコードを何度も書かない
量
■オーバーロードの実装例
//引数をとらないパターンのメソッド
int methodX()
{
//引数にデフォルトの値を補完して、実際に処理を行うメソッドを呼び出し、結果をそのまま返す
return methodX(10);
}
//引数を1つとるパターンのメソッド
int methodX(int arg1)
{
//第2引数にデフォルトの値を補完して、実際に処理を行うメソッドを呼び出し、結果をそのまま返す
return methodX(arg1, 20);
}
//最も多くの引数をとるパターンのメソッド
int methodX(int arg1, int arg2)
{
//実際の処理を行う
return arg1 * arg2;
}
24
スコープをうまく使って変数名の衝突を避ける
わ 量
■ややJava特有の話になる。
■スコープとは{}で囲まれている範囲のこと。
メソッド定義や、forやwhileを使うときは自然と使うことになる。
■あまり知られていないが、Javaではforやwhileのない箇所でもスコー
プを使える。
■処理の区切りがはっきりするので、わかりやすさの向上にも役立つ。
■少し見慣れない手法なので、後から見た人に意図を伝えるために、
「(深い意味はないですが、変数名の衝突を避けるためにスコープを
分けています)」
といったコメントを添えておくと良い。
→続く
25
スコープをうまく使って変数名の衝突を避ける
わ 量
◆Before
◆ After
//A関連の処理
List<Integer> list = getList();
int count = list.size();
Integer firstValue = list.get(0);
//A関連の処理
//(深い意味はないですが、変数名の衝突を避けるためにス
コープを分けています)
{
List<Integer> list = getList();
int count = list.size();
Integer firstValue = list.get(0);
System.out.println(count + ", " + firstValue);
//B関連の処理
int count2 = getCount();
int firstValue2 = getFirstValue();
System.out.println(count + ", " + firstValue);
}
//B関連の処理
//(深い意味はないですが、変数名の衝突を避けるためにス
コープを分けています)
{
int count = getCount();
int firstValue = getFirstValue();
System.out.println(count2 + ", " + firstValue2);
System.out.println(count + ", " + firstValue);
}
変数名の衝突を避けるために仕方なく
末尾に「2」をつけている(危険)
スコープ毎に変数は独立しているので、
変数名の衝突の心配はいらない
26
シ わ
最適化にこだわり過ぎない
■最適化は、シンプルさやわかりやすさを犠牲にすることが多い。
■あらゆる箇所を最適化したくなる誘惑にかられるが、実際はボトル
ネック以外の箇所を頑張って最適化してもほとんど効果はない。
■勘違いしてもらいたくないが、必要な箇所以外の最適化は全て放
棄しろと言いたいわけではない。
実装にかかるコストと、実際の効果の兼ね合いを考えるのが重要だ
ということ。
■努力には、正しい努力と無駄な努力がある。
無駄な努力は自己満足にすぎず、時間と労力を浪費するだけ。
27
なんでもかんでも自作しようとしない
シ
量
■スクラッチで一からコードを書けるのは大事な事だが、過去の遺産
を使いまわした方が手早く済むし、ミスも起きにくい。
■使いまわしの元ネタを蓄えておいたり、ネットから素早く検索して探
し出すのも、プログラマーの大事な能力の一つ。
■汎用的なアルゴリズムやデータ構造についての処理は、自作しなく
ても探せば大抵どこかにある。
そして、大抵それは自作するよりも出来がいい。
■趣味で自作する分には個人の自由なので止めはしないが、仕事で
は絶対にやらないこと。
■特にセキュリティや暗号化に絡む分野の場合、絶対に素人が手を
出してはいけない。
28
シ わ 量
全体に統一性を持たせる
■シンプルなプログラミングを心がけていると、特別ロジックが複雑な
箇所以外は自然と似通ったパターンのソースになってくる。
■統一性のあるソースは、処理が把握しやすいだけでなく、量産にも
向いている。
■統一性のあるソースは、後から修正する場合においても、大体同じ
ように修正すれば済むので、メンテがしやすい。
■プログラミングとは何かを突き詰めていくと、いかに効率の良いパ
ターンにはめていくかという思考パズルとも表現できる。
29
ソースを修正する場合は一箇所ずつ確実に行う
シ わ
■一気に複数の箇所のソースを修正してしまうと、問題が発生した場
合に何が原因だったのかわからなくなってしまう。
■一箇所修正するごとに動作確認を行なっていけば、問題が発生し
ても原因がはっきりしているので対処しやすい。
■変更前のソースとの差分を比較して、余計な修正が入っていない
かを確認する方法も大変有効。
特にSubversionのようなバージョン管理システムを使っている場合
は、コミット前に必ず差分を確認する習慣をつけておいた方がいい。
■「急がば回れ」、「急いては事を仕損じる」ということわざがぴったり
と当てはまる。
30
人の手を介する作業をなるべく減らす
量
■人の手を介する作業は、単に手間がかかるというだけでなく、それ
だけミスが発生する可能性が増すという危険をはらんでいる。
■しっくりくるツールを見つけ出し、上手く使いこなして作業を効率良く
進めるのもプログラマーの大事な能力の一つ。
■特にソースのフォーマット整形や、変数名の変更といった機械的な
リファクタリング作業は、ツールを使うことにより素早く・正確に・確
実に行うことができる。
■ソースの手入力もなるべく減らして、エディタによる変数やメソッドの
補完機能や、スニペット機能の活用を心がけるといい。
スニペット機能とは、繰り返し使うコード断片を登録しておくもので、
例えば「for」と入力するだけで、for構文一式の雛形を自動入力して
くれたりする機能のこと。
31
シ
コンパイルエラーを味方につける
量
■コンパイルエラーを嫌がる人もいるが、プログラマーにとってこんな
ありがたいものはそうそう無い。
コンパイルのないインタプリタ系の言語を使うと、コンパイルエラー
のありがたみがよくわかる。
■少しコーディングしたら即コンパイルという習慣をつけておきたい。
今どきのマシンなら、長くても数秒でコンパイルが完了するので、ミ
スを素早く発見できることを考えると、十分お釣りがくる。
■チェック対象を増やすために、コンパイル時の警告レベルはなるべ
く上げておいた方が良い。
■リフレクションを駆使したプログラミングは便利だが、コンパイルエ
ラーの恩恵を受けられなくなるというデメリットも持ち合わせている。
32
量
プロトタイプこそしっかり作る
■プロトタイプを作る際に、「どうせプロトだから」と手を抜いて適当に
作る人が多いが、大変よろしくない。
■プロトタイプは量産のベースになる大切なものなので、これをしっか
り作っておくと全体的な品質が高まる。
■プロトタイプは一機能分や一画面分だけでなく、複数作っておく方
がいい。
そうすることで、共通の機能と個別の機能を早い段階で見極めるこ
とができる。
33
面倒だと思う作業をそのままにしない
シ
量
■「面倒が嫌い」というのはある意味プログラマーにとって大事な感情。
■面倒な作業をダラダラと続けるときにこそ、ミスは発生しやすい。
■何でも力技で押し切ろうとするのではなく、面倒を減らすうまい仕組
みを考えたり、作業を自動化しようと工夫することが大切。
そうして生み出された仕組みは、今後も大いに役立つ財産になる。
■面倒な作業というのは大抵量産段階で発生するので、これを効率
化することができれば、品質・開発速度を大いに向上させられる。
34
シ わ 量
動作確認は早めに自分で行う
■動作確認はテスト段階になるまでろくにせずに、しかも他人任せに
する人も残念ながら多いようだが、なってないと言わざるを得ない。
■「プログラムにはバグがあって当然」というのは残念ながら正しいの
かもしれないが、他人に迷惑をかける前に自分で何とかしようとす
るのが真っ当な神経のプログラマー。
他人にバグを指摘されても恥ずかしいと思わなくなったり、申し訳が
ないという感情が湧かなくってしまったら、悪い意味で慣れてしまっ
て正常な感覚を失っている危険な状態と言える。
■問題は早めに解消しておかないと、問題のあるソースがコピペされ
たりしてどんどん拡散してしまう。
そうなってから修正してまわるほうが、よっぽど大変。
→続く
35
シ わ 量
動作確認は早めに自分で行う
■自分で動作確認をするのを推奨するのは、プログラムの内容を一
番よく知っているのは、他ならぬ自分だから。
どういうことをすると危険かは人一倍わかっているはずなので、厳し
い条件で動作確認ができる。
■JUnitのような自動テストの有効性は大いに認めるが、決して万能
ではないことも理解しておくこと。
「テストケースのないソースは認めない」といった自動テスト原理主
義者もちらほらいるようだが、現実に即していない。
特に、画面に表示される内容や、DBなど外部のデータに左右される
もの、ユーザーの操作が伴うものは非常に自動テストしづらい。
テスト対象の特性に合わせて、最もコストパフォーマンスの高い動
作確認の方法を選択することが大切。
36
他人にソースを見られることを嫌がらない
シ わ 量
■他人にソースを見られることを嫌がる人は多いが、実によろしくない。
むしろ自分から積極的に見てもらうぐらいの心意気が大切。
■ソースを見られるのが嫌というのは、自分のソースに自信がなかっ
たり、何かやましいことがある証拠である。
■他人に見られても恥ずかしくないものを作ろうという意識があれば、
自然とより良いコーディングにつながる。
特に、シンプルでわかりやすいソースを書こうという意識が高まる。
■他人にソースを見てもらうということは、有益なアドバイスをもらえる
大きなチャンスでもある。
■見るのを頼まれた場合も、他人のソースを見て色々と指摘する行
為は得るものが大きいので、なるべく断らないこと。
37
最後に
■コーディングはおろそかにされがちだが、プログラミングの基礎にし
て究極というべき大切な技能。
■どんなに良い要件定義や設計をしても、コーディングがダメなら全
てぶち壊しになってしまう。
■プログラマーを名乗るからには、常に良いコーディングを意識して
行うようにしよう。
それでは、良いプログラミング生活を!
38