アーカイブ

‘Programming’ タグのついている投稿

よくないプログラムコメントの書き方について

2008 年 9 月 23 日


プログラムのコメントというのは、人によって書き方は様々である。

otsune コードでhowやwhatは書けてもwhyは表現できません!はてなブックマーク数

と、otsune さんも言うように、俺が考えるプログラムのコメントというのは、「summary」や「why?」を書くべきであって、決して「what?」をひたすら書くべきではない。
(この場合の「summary」というのは、「what」の概要のようなものかもしれないし、コーディングの思想のようなものかもしれない。そして、「why」は、仕様的な「何故」かもしれないし、トリッキーな方法を説明する為の「何故」かもしれない)

そもそも、「如何に(後に役に立つ)ドキュメントを残すか」や「プログラムにどのようにコメントを書くか(又は書かないか)」というのは、延々と議論が繰り返されているテーマであり、別にその正解を提示しようとする訳ではなく、あくまで個人的な意見であることを書き加えておく。

という訳で、今回は俺があまり役に立たないと思うようなコメントをあげてみよう。

イヤなコメントの付けかたの例

例1)
/* a に b を代入 */
int a = b;
読めばわかる。


例2)
// モードが MODE_XXX で フラグが 1 でなく、kbn が 1 又は 2 の時
if (((mode && MODE_XXX) == MODE_XXX) &&
(flg != 1) &&
(kbn == 1 || kbn == 2)) {
読めばわかる。
(逆に「何故」そのような判定なのかがわからない)


例3)
try {
// DBコネクションを取得します
conn = DB.getConnection();
// トランザクションを開始します
tran = conn.begenTran();
// SQLを取得します
String sql = this.makeSQL();
// SQLを実行します
int ret = conn.execute(sql);
// コミットします
conn.commit();
// 処理結果を返します
return ret;

// 例外を補足します
} catch(Exception e) {
// ロールバックします
if (conn != null) { conn.rollback(); }
// ログを出力します
LogHelper.writeError(e);
}
まるで古文か英文の原文と口語訳のように説明書きがある。
(ロジック読みにくすぎ。どうしても説明したいなら、メソッドの先頭かブロックの先頭で summary にするべき)


まぁ、他にも
・ 書いてあるコメントが間違えている

・ コメント情報が古くて最新の状態を反映していない
などもあるのだが、これでも「ないよりマシ」なモノもあるため、一概にはなんともいえない。

プログラムのコメントというのは、遊びで作るプログラムや、利用目的がはっきりして比較的小さなプログラムなどには、Summary 以外ほとんど必要ないと思うのだが、どうしても業務プログラムや大きなプログラムになってくると必要になってくるものである。
しかし、書くとなっても、「仕様書」に書いてあることと重複してダラダラ書いても意味がないし、かといって全く仕様を書かないとなると「仕様書がどこまでメンテされているのか」ということも影響してくる。

自分自身、これからもプログラムを作る時は(今までも気をつけてはいるが)、プログラムのコメントのみならず、如何に生産性・品質・保守性などを効率よく行うことができるか。如何に他人に情報を伝達することができるか。などを考慮しながら取組みたいものである。

特に大人数が関わるプロジェクトなら尚更です。





Programming

俺的コーディングルール SQL編

2007 年 1 月 19 日


 プロジェクトのコーディングルールがこうでなければいけないとか、他人に強制するわけではないが、自分自身で一貫性の無いコードを書くのは気持ち悪いので、オレオレルールを決めてたりする。大抵は デ・ファクト的なルールに沿う形で書くことが多いのだが、SQL や PL/SQL に関してはなかなかデファクトと呼べるものがないので(あるのか?)、メモ的に書きとめておく。

原則
  1. キーワード小文字
  2. オブジェクト名大文字
  3. カンマは後ろ
  4. インデントは半角スペースで 2
  5. 一つの SQL 文でキーワード毎にインデントしない(副問合せ除く)
 まず、1.2. に付いてなのだが、昔は「キーワード=大文字」という意味不明な先入観で大文字で書いていた。ただ、それだと PL/SQL のキーワードも大文字、オブジェクト名も大文字で結局ほとんど大文字になってしまうのと、Shift 押すのが面倒という理由で小文字に変えた。では何故オブジェクト名は大文字のままかというと、これは利用しているツールの問題と、つづり間違いを極力減らす為にオブジェクト名はなるべく Copy & Paste するからであり、それほど負担にはならないという理由からだ。キーワード小文字との対比という意味でもよい。
 3. は、SQL は経験上、前カンマが慣習的に多い気がするのだが、どうも他のプログラムと並列していると後ろカンマに慣れてしまい、どうも前カンマが気持ち悪い。ただ、やはり前カンマにしようかな~などと悩ましいところである。(理由は後述)
 4. インデントがタブかスペースかについては、色んなところで議論されているのだが、俺はスペースを使う。色んな理由から。昔は 4 インデントをしていたのだが、HTML や XML などの入れ子構造や、ちょっと条件文を重ねたときに深くなりすぎるので、最近では 2インデントをよく利用してる。タブストップが無い環境やタブ文字が入ってしまう環境で編集するときにも楽だし(あんまりないけど)。
 
というわけで基本的なオレ的書き方。
select
f1.ID,
f1.ITEM1,
f1.ITEM2,
f2.ITEM3,
m1.USER_ID,
m1.USER_NAME
from
table_head f1,
table_detail f2,
user_master m1
where not exists (
select 1 from tableXXX s1
where s1.ID = f1.ID
)
and f1.ID = f2.ID
and f2.USER_ID = m1.USER_ID
and f2.ITEM3 = 'xxx'
order by m1.user_id, f1.id, f1.item, f2.item
気をつけているポイントとしては
  1. select, from の次は改行をつける(項目が単一の場合は除く)
  2. where は1個目の条件を書いてから改行
  3. 各キーワードはインデントせずに揃える
  4. from句のテーブル名のエイリアスは略語にしない
  5. 結合条件を先に書く
大抵の理由はインデントのしやすさというか、なるべく深くならないように気をつけるという点からきている。どういうことかというと
select f1.ID,
f1.ITEM1,
f1.ITEM2,
f2.ITEM3,
m1.USER_ID,
m1.USER_NAME
from table_head f1,
table_detail f2,
user_master m1
where not exists (
select 1 from tableXXX s1
where s1.ID = f1.ID
)
and f1.ID = f2.ID
and f2.USER_ID = m1.USER_ID
and f2.ITEM3 = 'xxx'
order by m1.user_id, f1.id, f1.item, f2.item
と書いてしまうと、2個目以降のオブジェクト名や、条件文に副問合せが発生した場合などにかなり深くなる。タブストップで揃わない位置に並べるのも面倒だ。
 オブジェクト(主にテーブル)のエイリアスは大抵つける、その際、テーブル名を略したエイリアスをつけない(TOKUISAKI_MST TOK, USER_MST USR みたいな)。理由は、略文字はテーブル名が増えたり、似たような名前のテーブルが並ぶと、ぱっと見で判断しにくいから。その点数字だとすぐ見分けが付く。(from 句を参照しないとだめだが) 頭文字の f というのは特に意味なし。

 たまに and を後ろにつける人を見かけるけども、あんまり好きじゃない。でも、 or は後ろにつけるのが好き。

 結合条件は from 句で innerjoin, leftjoin しても良いのだが、ORACLE を触ることが多かったので where 句に書くことにしている(今は ORACLE でもその書き方ができるので、そちらのほうが良いのかもしれないけど)。ただ、結合条件と抽出条件を考えたとき、変更が多いのは後者だし、リレーションは決まっているので 結合を先に書くというルールだけ。ただし、exists だけは先に書く。これは RBO の時の名残で。

 先ほども書いたが、最近悩んでいるのが、カンマの位置。大抵の言語では後ろカンマが標準的だと思うんだが、SQL に関しては 前カンマのほうが効率が良いのではないかと思っている。理由は開発中は取得フィールドの追加変更や切り貼りが多いから。というのも、前カンマだと 追加が簡単。後ろカンマだと、前のフィールドのカンマをつけなければならない。仕様的には、先頭のフィールドよりも追加のほうが多いし、先頭は Primary Key などが並んでいることが多いので、大抵決まっている。他の SQL 書くときにも Copy&Paste が簡単だからだ。
select
f1.ID
, f1.ITEM1
, f1.ITEM2
, f2.ITEM3
, m1.USER_ID
, m1.USER_NAME
, m1.NICK_NAME -- 追加
……
とまぁ、他人が読んでもとてもくだらない話だろうとは思うが、よく使う select 句に関してはこんな感じ。
そこのあなたはどんな事に気をつけてコーディングしているのだろうか、大変気になるところだ。





Programming

PL/SQL等のバッチ処理でたまに見かける最悪なプログラム

2007 年 1 月 18 日


 今日はプログラムの話 & 愚痴。

 DB更新などのバッチ処理で Pro*C 使ってたり PL/SQL 使ってたりするプロジェクトは結構多いが、あほかっ!と思ってしまうことがある。ソースが汚いとか main() や BEGIN END の間に全部の処理書いてるとか、そういうのはまだ我慢できる(ていうか、全部直してる訳にはいかないので)。では、何が気に食わないのかと言うとループ中にファイルにアクセスする感覚で SELECT 句発行してたりするプログラムが結構多いこと。

 ちょっと考えればわかるだろう!とか、仕様はどうなってんだ!とか、ソースレビューの段階で気づかないか?とか色々意見はあるだろうが、ちょっと大きめのプロジェクトなどでコーディングを丸投げしたり、プロジェクトに火がついてプログラマ大量投入して解決、みたいなところでは結構見かけてきた。んで、「バッチ処理が遅い」だとか「バッチ処理が重い」だとか言ってるんだが、当たり前だろう!

 例えば、具体的にどんなプログラムかっつーと

DECLARE
count number;
CURSOR cur IS
SELECT id FROM TRAN;
BEGIN
FOR c IN cur LOOP
-- マスタの値を取得
SELECT *
FROM MASTER
WHERE id = c.id;

-- なんかの処理 --

-- 存在チェック
SELECT COUNT(*)
INTO count
FROM TRAN2;

IF (count = 0) THEN
-- insert 処理
ELSE
-- update 処理
END IF;
END LOOP;
END;
みたいな。
これでカーソルが数万とかの単位になるとそれだけで重くなるし、他にもカーソルの値で他のテーブル検索して 1ループ 1秒前後かかるとしたら、単純計算で 1秒 * 1万回。普通に2~3時間くらいかかったり。Join しとけよ!

 つーか、大体 COBOL上がりの設計者で、PL/SQL 使ってバッチ作ろうって人に多いんだが(偏見です)、更新を行単位で処理しようとするんだな。どうしても必要な時は FETCH すりゃーいいけども、大抵の場合集合的に更新かけれるし、ムリならワークテーブルなりで対象データ絞れよな。Insert/Update 1万回するのと、1万行の更新を1回するのとの考え方の違いをわかってないっつーか。

 まぁ、俺がこんなに怒ってるのは、今まさにそんなプログラムをメンテしたりしてるからであり、このめんどくせーって思いと怒りをどこかの誰かと共有するために書いてる訳だが、こういうプログラム見たことない?
 
 「見たこと無い!」って言うあなたは、幸せモノです!





Programming