More Related Content
Similar to プログラムの処方箋~健康なコードと病んだコード (20)
プログラムの処方箋~健康なコードと病んだコード
- 10. プログラミングで大切なこと
それは…
「障害」と「機能改善」
です。
この2つは、
「それが起こるのが早いか、遅いか。」
というだけで、必ず起こります。
Accidents will happen.
- 23. 病んだコードの症状と処方箋
~こういうコードは要注意!
インデントがグチャグチャ
...
// 最大数まで処理したらループを抜ける。
int MAX_COUNT = 10;
int count = 0;
while(count < MAX_COUNT) {
if (flg==true) {
...
} else {
...
}
for (int i=0; i<list.size(); i++)
...
}
count++; // カウントを1増やす。
}
「あれ?なんか無限ループしちゃうんだけど、何で?」
- 24. 病んだコードの症状と処方箋
~こういうコードは要注意!
インデントがグチャグチャ
...
// 最大数まで処理したらループを抜ける。
int MAX_COUNT = 10;
int count = 0;
while(count < MAX_COUNT) {
if (flg==true) {
...
} else {
...
}
for (int i=0; i<list.size(); i++)
...
} ←このカッコ、for分に対応するようで、実は「while」のカッコ。
count++; // カウントを1増やす。
} ←このカッコがWhileに対応するようだけど、実は違うカッコらしい
- 25. 病んだコードの症状と処方箋
~こういうコードは要注意!
インデントがグチャグチャ
...
// 最大数まで処理したらループを抜ける。
int MAX_COUNT = 10;
int count = 0;
while(count < MAX_COUNT) {
if (flg==true) {
...
} else {
...
}
for (int i=0; i<list.size(); i++) {
... ←処理が1行で終わる場合でも、カッコを付けるスタイルが
一般的には好まれる。
}
} ←whileの開始に揃えてインデントを記述する。
count++; // カウントを1増やす。 ←この行は「whileの外」なのがわかります。
}
- 26. 病んだコードの症状と処方箋
~こういうコードは要注意!
インデントがグチャグチャ
「インデント」が揃っていないだけで、
プログラムの可読性が著しく低下します。
グチャグチャ
ぜったいダメ!
プログラムは必ずきちんとインデントする!
インデントを「タブ」または「スペース」とするか、
インデントの幅をいくつにするかは、
開発プロジェクト内で統一しておきましょう。
- 28. 病んだコードの症状と処方箋
~こういうコードは要注意!
重複したコード
// 正社員を出力
ArrayList empList = getEmpList();
Iterator it = empList.iterator();
while (it.hasNext()) {
String empName = (String) it.next();
System.out.println(empName);
}
// 協力会社を出力 ※バグあり
ArrayList empList2 = getEmpList2();
Iterator it2 = empList2.iterator();
while (it.hasNext()) {
String empName = (String) it.next();
System.out.println(empName);
}
「”社員名(empName)”にフリガナを付けて出力したいんだけど…。
もしかして、出力箇所を全部修正?(涙」
「似たような名前のメソッドがあるけど、なんかやな予感…。
やっぱり…両方直すのか…。欝だ。」
- 29. 病んだコードの症状と処方箋
~こういうコードは要注意!
重複したコード
// 正社員を出力
ArrayList empList = getEmpList();
Iterator it = empList.iterator();
while (it.hasNext()) {
String empName = (String) it.next(); 同じような処理が存在。
System.out.println(empName);
} 同じような処理が2箇所以上
// 協力会社を出力
あった場合、共通化を考える。
ArrayList empList2 = getEmpList2();
Iterator it2 = empList2.iterator();
while (it.hasNext()) { ←上の処理で利用した変 数を利用している。(バグ)
String empName = (String) it.next();
バグ発見!
System.out.println(empName);
}
- 30. 病んだコードの症状と処方箋
~こういうコードは要注意!
重複したコード
// 正社員を出力
outputEmployeeNameList(permanentEmpList);
// 協力会社を出力 共通化することで、
outputEmployeeNameList(partnerEmpList);
多岐に渡る修正等が発生しに
※共通可したメソッド くくなる。
public void outputEmployeeNameList(List<String> list) {
for (String s : list) {
System.out.println(s);
}
※余談ですが・・・
}
Iteratorを利用する場合、while文はバグの温床になりやすいので、
for文、可能であれば拡張forループ(JDK 1.5以降)を利用したほうが良いです。
- 31. 病んだコードの症状と処方箋
~こういうコードは要注意!
重複したコード
「重複したコード」は、プログラムの
「コピー&ペースト」で発生しやすいです。
プログラムの「コピペ」をする前に、
そのプログラムの意味をよく理解して、
できるだけ「共通化」を考えましょう。
- 33. 病んだコードの症状と処方箋
~こういうコードは要注意!
分かりづらい命名
int akb = 48;
String h = c + ":" + n;
private void getENM(int akb) {
…
}
「”akb”って、なんやねん!!!」
「hには何が代入されてるんだろう・・・。」
「getENM…なるほど・・・わからん!」
「get?何が返ってくるのだろう…。え?戻り値なしかよ!w」
- 34. 病んだコードの症状と処方箋
~こういうコードは要注意!
分かりづらい命名
int akb = 48; ←書いた自分にしか分からないことは書かない。
String h = c + ":" + n; ←変数名が短すぎてわからない
private void getENM(int akb) { ←メソッドの役割と異なる名前
… ※getXXXなのに値を返さない。
}
- 35. 病んだコードの症状と処方箋
~こういうコードは要注意!
分かりづらい命名
int departmentNo = 48;
String headerName
= String.format("%s:%d", departmentName, departmentNo);
private void outputEmployeeNameList(int departmentNo) {
…
}
変数名は多少長くても、分かりやすく書く。
メソッド名やクラス名は役割にそぐった名前を付ける。
- 36. 病んだコードの症状と処方箋
~こういうコードは要注意!
分かりづらい命名
例外①…慣用句
for (int i=0; i<list.size()-1; i++) {
for (int j=0; j<list.size()-1; j++) {
for (int k=0; k<list.size()-1; k++) {
}
}
}
慣用的に使われるfor文の「ループ変数」等は、
「i, j, k」のほうがわかりやすいかもしれません。
- 37. 病んだコードの症状と処方箋
~こういうコードは要注意!
分かりづらい命名
例外②…スコープの短い変数
while (employeesRecordSet.next()) {
emp.setName(employeesRecordSet.getString("employee_name"));
}
while (rs.next()) {
emp.setName(rs.getString("employee_name"));
}
パッと見で処理が見渡せるようなケースで「スコープが短い変数」を
利用する場合、変数名が長いと逆に冗長かもしれません。
- 38. 病んだコードの症状と処方箋
~こういうコードは要注意!
分かりづらい命名
変数の命名方法にはいくつか「記法」が存在します。
Pascal(パスカル)形式
各単語の頭文字を大文字で記述する。
例)EmpName
Camel(キャメル)形式
各単語の頭文字を小文字で記述する。
例)empName
Hungarian(ハンガリアン)記法
型の情報だけで補えない情報を補う。
例)yenPay, dolPay
型の接頭辞を付与する。(iはint, sはString等)
例)iEmpCode, sEmpName
- 39. 病んだコードの症状と処方箋
~こういうコードは要注意!
分かりづらい命名
これは私の主観になってしまいますが、
通常の変数であれば、Camel形式に加えて「ドメイン」を
よく考えておいて変数名を付けると便利です。
「ドメイン」とは…型の集合のこと、DBの論理設計で利用されます。
下記の例のように、グループと型をあらかじめ決めておきます。
Name(名)…文字(32) Date(日)…日時
Type(種別)…文字(2) Amt(金額)…数値(BigDecimal)
ID(番号)…数値(int)
No(番号)…数値(int)
Flag(フラグ)…ブール(true/false)
- 40. 病んだコードの症状と処方箋
~こういうコードは要注意!
分かりづらい命名
「xxxNameはこの型」とドメインを決めておくと、プログラムを
読みながら、そこから変数の型をかんたんに推測できます。
※DBの論理設計とあわせて考慮すると、とてもコーディングがしやすくなる。
Name(名)…文字(32) …empName(社員名),userName(ユーザ名)
Code(コード)…文字(8) …categoryCode(カテゴリコード)
Type(種別)…文字(2) …categoryType(カテゴリ種別)
ID(番号)…数値(int) …empID(社員ID),userID(ユーザID),customerID(顧客ID)
No(番号)…数値(int) …salesNo(売上番号), lotNo(ロット番号)
Seq(通番)…数値(int) …orderSeq(注文通番), receiptSeq(領収書通番)
Flag(フラグ)…ブール(true/false) …deleteFlag(削除フラグ)
Date(日)…日時 …startDate(開始日), issueDate(発行日)
Amount(金額)…数値(BigDecimal)…salesAmount(売上金額)
- 41. 病んだコードの症状と処方箋
~こういうコードは要注意!
分かりづらい命名
変数名やメソッド名について記法をいくつか紹介しました。
ここではそれぞれを詳しく説明しませんが、
どれも一長一短あるので、
どれがすぐれていると一言では言い難いです。
しかし、どの記法を用いるにしても、
大事なことは、プロジェクトで記法を統一し、
わかりやすい名前を付けることです。
- 43. 病んだコードの症状と処方箋
~こういうコードは要注意!
長~いメソッド
private void DataGrid1_ItemCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e) |
DataView dv = new DataView(dt);
ViewState["Sort"] = dv.Sort = sort;
DataGrid1.DataSource = dv;
DataGrid1.PageSize = viewnum;
if (dt.Rows.Count < (DataGrid1.PageSize * DataGrid1.CurrentPageIndex))
DataGrid1.CurrentPageIndex = (int) (dt.Rows.Count / DataGrid1.PageSize);
if ((dt.Rows.Count == (DataGrid1.PageSize * DataGrid1.CurrentPageIndex)) && (dt.Rows.Count != 0))
DataGrid1.CurrentPageIndex = DataGrid1.CurrentPageIndex - 1;
DataGrid1.Visible = true;
if (dt.Rows.Count != 0) {
StringBuilder sb = new StringBuilder();
sb.Append(dt.Rows.Count.ToString()).Append("件");
FindRecordsLabel.Text = sb.ToString();
} else {
FindRecordsLabel.Text = AllegroMessage.S10002;
}
if (searchCondition.HasErrors) {
setMessageLabel(searchCondition.ErrorMessage);
}
「300行…508…1,078…。ま、まだ続いている!
い・・・いったい、どこまで続くというんだ!?」
- 44. 病んだコードの症状と処方箋
~こういうコードは要注意!
長~いメソッド
private void DataGrid1_ItemCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e) |
DataView dv = new DataView(dt);
ViewState["Sort"] = dv.Sort = sort;
DataGrid1.DataSource = dv;
DataGrid1.PageSize = viewnum;
if (dt.Rows.Count < (DataGrid1.PageSize * DataGrid1.CurrentPageIndex))
DataGrid1.CurrentPageIndex = (int) (dt.Rows.Count / DataGrid1.PageSize);
if ((dt.Rows.Count == (DataGrid1.PageSize * DataGrid1.CurrentPageIndex)) && (dt.Rows.Count != 0))
DataGrid1.CurrentPageIndex = DataGrid1.CurrentPageIndex - 1;
DataGrid1.Visible = true;
if (dt.Rows.Count != 0) {
StringBuilder sb = new StringBuilder();
メソッドが長すぎると、
sb.Append(dt.Rows.Count.ToString()).Append("件");
FindRecordsLabel.Text = sb.ToString();
} else {
FindRecordsLabel.Text = AllegroMessage.S10002;
処理内容の把握がとても難しくなります。
}
if (searchCondition.HasErrors) {
setMessageLabel(searchCondition.ErrorMessage);
}
また、改修の影響範囲なども見えにくく
なってしまいます。
- 45. 病んだコードの症状と処方箋
~こういうコードは要注意!
長~いメソッド
// 検索条件を設定する
Condition condition = new Condition();
condition.empID = 10;
// 取得した社員一覧を出力する コメントを書いた処理があった場合、
List<Employee> list = search(condition);
そのコメントを「メソッド名」とした
for (Employee employee : list) {
... メソッドにしてしまえば、
}
コメントもいらなくなる。
setSearchCondition();
outputEmployeesList(condition);
- 46. 病んだコードの症状と処方箋
~こういうコードは要注意!
長~いメソッド
メソッドを短くするときは、
「コードを短くする」とは考えず、
「メソッド名とその実装の距離を縮める」
と考えるのがポイントです。
また、メソッドの分割を考えたとき、
「メソッドの命名に困ってしまう」ということは、
「そのメソッドにはまだ分割の余地がある」
ということです。
- 48. 病んだコードの症状と処方箋
~こういうコードは要注意!
多すぎる引数
/**
* xxxを出力します。
* 出力内容は引数の社員種別とxxxフラグによって制御し、どうたらこうたら…
*/
private void printEmpInfo (
int empID,
ずらずらと引数が多すぎる。
string empName,
int empType,
...,
...
) {
...
「ど、どうやって使うんだこのメソッド・・・。」
「引数がたくさん・・・、わかりづらいなぁ。」
- 49. 病んだコードの症状と処方箋
~こういうコードは要注意!
多すぎる引数
/**
* xxxを出力します。
*/
private void printEmpInfo (Employee employee) {
System.out.println(employee.getEmpID);
...
関連のある情報はクラスにする。
オブジェクトを引数に渡す。
- 50. 病んだコードの症状と処方箋
~こういうコードは要注意!
多すぎる引数
メソッドに引数が多すぎる場合、
「引数にオブジェクトを利用する」と
きれいにまとまります。
渡す値が増えた場合でも、メソッドの
呼び出し元の修正が不要です。
反面、メソッドが「引数クラスに依存」してしまうという
デメリットもあることも意識しておきましょう。
- 52. 病んだコードの症状と処方箋
~こういうコードは要注意!
不適切なコメント
/**
* xxxを出力します。
* 出力内容はフラグによって制御し、どうたらこうたら…
* ○○の場合、xxxxx。△の場合、xxxxx。
* このケースの場合、xxxxxで、xxxxする。
* それ以外の場合、xxxxxx。
*/
private void koreHaMuzukashiiSyoriDesuYo(…) {
// 最初にxxxxします。
...
// 下記の処理では、xxxをxxxして、その後xxxx。
...
// この変数にはxxxxが格納されているので、xxxxxの後にxxxxします。
...
「ちゃんとコメント書いてあるけど・・・んんっ!?」
- 53. 病んだコードの症状と処方箋
~こういうコードは要注意!
不適切なコメント
/**
* xxxを出力します。
* 出力内容はフラグによって制御し、どうたらこうたら…
* ○○の場合、xxxxx。△の場合、xxxxx。
* このケースの場合、xxxxxで、xxxxする。 コメントを書くこと自体、
* それ以外の場合、xxxxxx。
*/ まったく悪いことではないですが、
private void koreHaMuzukashiiSyoriDesuYo(…) {
// 最初にxxxxします。
...
きをつけなければならないことが
// 下記の処理では、xxxをxxxして、その後xxxx。
... あります。
// この変数にはxxxxが格納されているので、xxxxxの後にxxxxします。
...
- 54. 病んだコードの症状と処方箋
~こういうコードは要注意!
不適切なコメント
コメントはコードでは表せない説明の為に書きます。
「コメントがたくさん書いてある」ということは、
「それが分かりづらい処理である」ということです。
コメントを解りづらい処理の「消臭剤」の為に使ってはダメ。
そういった処理はそもそも、ロジックを見直しを検討が必要。
自分で「たくさんコメントを書いているな。」と思ったら、
いったん手を止めて、設計やロジックを見直しましょう。
- 55. 病んだコードの症状と処方箋
~こういうコードは要注意!
不適切なコメント
コメントには、プログラムが処理を進めていく
具体的な方法を書くべきではありません。
処理内容の説明ではなく、
なぜそのように書かれているのか?
という理由や、
最終的に何が達成されるのか?
を説明する。
× // UserRegistryのデータでRegistryオブジェクトを更新する。
○ //登録情報を後で参照できるようにキャッシュする。
- 56. 病んだコードの症状と処方箋
~こういうコードは要注意!
コメント
その他、コメントについては、
過去の勉強会
「第4回 良い?悪い?コードコメントの書き方」
で詳しく取り上げています。
興味のあるかたは、そちらもご覧くださいな。