第15話 コーディング規約は必要か?
少しご無沙汰しています。
今回からまた新しいシリーズです。ソフトウェアに関係する事柄のあれこれについて書いてみたいと思います。3回程度の予定です。
今回はコーディング規約についてです。コーディング規約がどんなものかはプログラマやSEなどを経験していれば知らない人はまずいないと思います。会社としてきちんとドキュメントになっているところもあれば、部や課、プロジェクト単位で適用している所もあるようです。
ただ、わたしの経験では、なぜか、なかったところの方が多かった気がします(気がするというのは実際はあったが見たことがなかっただけかもしれないので。もっともそれじゃないのと同じですが)。
現在のわたしの状況に関しては、「なかった」ので、わたしが作ってメンバーに周知しています。わたしが作るコーディング規約に関しては基本的にA4用紙に1枚程度の量で、そんなにきつく縛っていません(たぶん)昔ほどコーディング方法がリソース不足や速度低下に直結しないので誰が見ても(=多少スキルが低い人が見ても)分かりやすくコーディングしてね、程度のものです。でも、案外「分かりやすく」っていうのがクセ者だったりします。
■管理者の立場から見たコーディング規約
わたしは世のマネージャクラスの人がどの程度メンバーのリソースを見ているのかは分かりませんが、わたしはテスト工程の前に動作チェックを兼ねながらメンバーのコーディングチェックをします。プロジェクトが大きいと不可能でしょうが、わたしの場合は幸か不幸か規模的には大きくないので丁寧に見ても2、3日程度で完了します。
自社プロダクト製品で、しかもリリース後は次バージョンのプロジェクトが走り始めます。開発メンバーが同じなら問題ないですが、違ったり外注したりすると、見にくいコーディングが存在した場合、担当者がそこを理解する時間が余計にかかるため、できれば統一してスッキリさせたいという思惑があります(実際はスッキリしていても多少時間はかかるでしょうが)。
また、複雑なコーディングはバグを内包しやすくなります。実際、どのレベルが「複雑」かというのは担当者の感覚によるところが大きく、自分のプログラミングスキルの高さを誇示したがる人ほど短縮したコードを記述したがる傾向があります。しかも、それが「当たり前」のような顔をしているので管理する方からすると冗談ではありません。
そう考えると、管理者から見るとコーディング規約は「必要」だといえます。
■プログラマから見たコーディング規約
プログラム開発におけるコーディングは「道順」と同じで出発地(仕様書)から目的地(プログラム完成)までの道筋を作成するものです。当然、そこがプログラマの腕の見せ所であり、表現するところになります。例えば、分岐する場合(状況によりますが)if文かswitch文か、ループの場合はfor文かwhile文か、いくつか選択肢が出てきます。コーディングは極論してしまえばそういった制御文や関数を組み合わせて水が流れるごとくスムーズな処理(組み合わせ)を考えることがある意味醍醐味だといえます。
昔はインプットとアウトプットさえ守られていれば中身を問われることはあまりありませんでした(わたしだけ……かな?)そういう意味ではコーディング規約は手枷足枷となる可能性があります。
プログラムが好きな人はよく「美しいコーディング」を追及するといいます。わたしも兼業PGなのでそれは理解できます。省略した形が美しいかどうかは分かりませんが、それでも我流だと美しい以前に分かりづらいものになってしまいがちです。これも美しいという基準が担当者の感覚によるところが大きいからです。
プログラマの仕事は、究極的には設計書通りにバグなしで早く動作する(ストレスがない動作をする)プログラムをコーディングすることだといえます。
ここに生産性を加えて考えるなら我流でもかまわない(生産性は慣れた方法が一番効率が良い、と考えるため)でしょう。いちいち気にしていたら進まないばかりか手戻りもありえます。慣れないとそこにバグを作り込むことも考えられます。
そう考えると、プログラマから見るとコーディング規約は「ない方」が理想といえます。
■コーディング規約の問題点
さて、ここからが本題です。
わたし自身、コーディング規約は必要か? と聞かれれば「おそらく必要」と答えます。
特にシステム完成後のメンテナンスを自分でやるならともかく、完成後は他者がメンテナンスすることの方が確率的にも高いと思います。そういう面から考えても一定のルールがないと読みづらいソースコードは保守性が悪いです。保守性が悪いと機能追加も行いにくく、追加や変更を行う事で新たなバグを生み出しかねないからです。
いろいろな職場を経験してわたしが感じるコーディング規約の一番の問題点は「コーディング規約が存在してもあまり守られていない」ことです。
まるっきり無視ではないのですが、コーディングチェックしていくと段々モラトリアム状態になっていくことが分かります。ようするに最初は守ろうと思うのですが、スケジュールが進んで切羽詰まってくると取りあえず完成を目指すためにコーディング規約が置き去りになり、そのままになってしまうのです。
コーディング規約にも問題が存在する場合があります。
コーディング規約は大別すると言語仕様に関係するものとそうでないものがあります。例えば命名規則なんかは基本的に言語仕様にあまり関係ありませんが(言語によっては大文字、小文字などの使い方に慣習がありますが)、以前書いた例外なんかはJavaなどにはあってもC言語ではありません。確かにC++、Java、C#などは言語の仕様は非常によく似ていますが、それぞれ考え方などは微妙に異なっています。そう考えると今の時代、コーディング規約を細かく策定するならば、頻繁にメンテナンスしないとすぐ時代遅れの使えないコーディング規約になってしまう可能性があります。極端な話、何十年も同じコーディング規約をメンテナンスせず使い続けるには無理が生じます。
ソースコードの性格を考えると、基本的に「見やすい」コーディングをプログラマは考えるべきだといえます。ソースコードはあくまでもプログラムを理解しやすい方法(高級言語)で記述しているに過ぎません。ビルドしてバイナリ(細かく言ったらキリがありませんが、ここではJVMや.NETで動作するものも含みます)を作成して初めて「動く」プログラムになります。リソース不足や速度がハードウェアやOSで吸収できるのならば、ソースコードはそれに甘えてしまうことは決して悪いことではないと思います。むしろ甘えられなくなった時にこそ本当にプログラマの腕の見せ所であり、その時にコーディング規約を守れないのならソースコードにコメントを残してコーディングすることは必要でしょう。システムの動作パフォーマンスを満たさなければそれは本末転倒ですから。
そう考えると、コーディング規約というのは意外と扱いが難しいものだといえます。
■どこまで規約にするか?
コーディング規約を作るのは一般的には「保守性」や「可読性」を上げるためです。確かに統一した方が見やすく、一定品質のソフトウェアが期待できるのは間違いないと思います。では、一体どこまでを「規約」にするか? が問題になってきます。
コーディング規約は普通、「命名規則」「コーディング規則」「禁止事項」の3つに大別されると思います。わたしは主に「コーディング方法」と「禁止事項」をメインに考えています。「命名規則」がないわけではありませんが、これは慣習的な要素が強いと思うので、確認事項的に明記します。
それぞれ、わたしがいつも考える規約を作るうえでの基準は、次の通りとなります(あくまでもわたし個人と職場が基準ですので、どこでも当てはまるとは考えていません。また、言語はJavaやC#を想定してください)。
【命名規約】
「変数」「定数」「関数名」「クラス名」「空間名」「ファイル名」などがあると思いますが、基本的にそれ「らしい」名前なら良いと思っているのであまり書くことはしません。なぜなら、言語ごとに暗黙の了解があり、プログラム的にもループ変数などはi、j、k……がある意味デフォルト的でもあるので改めて書くかどうかはメンバーの経験で考えます。定数が大文字ってのも同じでしょうか。
命名については担当者によって考え方にやや幅があります。こだわる人は「関数名はこうあるべき」「クラス名はこうあるべき」などきちんと自分で考えたルールがありますが、こだわらない人は本当に「適当」です。私の経験で関数を「func01()」「func02()」……、変数を「wk01」「wk02」……と付けていた人がいました。なぜ、そうしたか聞いてみると「考えるのが面倒だから」「プログラムの動作には関係ないから」だそうです。逆にこういうメンバーばかりならある程度規約が必要かもしれません。
【コーディング規則】
コーディング規則は主にインデントやかっこの付け方でしょうか。わたし個人では、かっこの付け方と分岐命令(if文)について明記します。
・かっこ
かっこ(中かっこ)については制御文や処理の後で改行するかしないかだと思いますが、わたしは改行させます。理由はわたしが見やすいから(笑)。隙間が多く、1行のボリュームが少ない方が分岐やループではかたまりができて見やすいです。あと、印刷するとあれこれメモ書きがしやすいのもあります。
・分岐命令
if文の事ですが、if文には2つの規約を明記します。1つは「条件式は比較演算子で比較すること」です。語句だけ見ると当たり前なんですが、以下のコーディングを推奨します。
if(flag == false)
{
// 処理
}
実際は「flag == false」じゃなくて「!flag」だけでもOKですが、パッとみてどっちか区別しやすいように記述させます。当たり前ですが、ビルドするとどっちも同じバイナリができあがります。
もうひとつは「絶対に通らない処理は作らない」です。言葉にすると至極当然ですが、これは以下のようなコーディングを想定します。
if(flag == true)
{
// 処理1
}
else if(flag == false)
{
// 処理2
}
else
{
// 処理3
}
どう考えても「処理3」は通りません。boolが一番分かりやすいので例を挙げましたが、整数型などでも時々「通らねぇだろ、これ」と思うコーディングがあります。例外でもそうですが「念のため……」というコーディング(らしい)です。念のためというのはあくまでも例外であって、true/falseしか条件がないのに3つ処理が用意されている事の方が不自然です。ただ、最近この「念のためコーディング」が増えてきたのは間違いなく、そういう意味で私は最近規約にしてあります。
【禁止事項】
わたしの場合、ボリューム的にはここが一番多いと思います。プロジェクトに合わせていろいろ考えますが、大体以下のような感じになります。
- グローバル変数は使用しない
- クラスのメンバ変数はpublicにしない
- ハンガリアン記法は使用しない
- ローカル変数の使い回しはしない(ループ変数など)
- 例外をパラメータチェックに利用しない
- gotoは使用しない
こんなところでしょうか。最後の「goto」はメンバーが若ければあまり書きません。というのはイマドキの若い者はそもそも「goto」を使わないプログラムが普通であり、わざわざ禁止事項にしなくてもまず出てこないからです(知らない人も意外と多い)まぁ、年齢が高くてもJavaやC#を普通に使っていればまずgotoに出合うことはないと思います(Javaってサポート外だったっけ?)C言語ではたまに見かけます。
個人的には「goto」は嫌いではないのですが(BASICの「GOTO」やアセンブラのジャンプ命令に馴染んでいたので)それはそれ、仕事は仕事なので。
「ハンガリアン記法」は割合最近書くようになりました。Microsoftでも昔はWinAPIやMFCでは当たり前のように用いていましたが、現在(.NET Framework)では使用しない事が(MSDNで)明記されています。変数名で変数の型が分かるのはC/C++でポインタを使うと便利なのですが、JavaやC#では確かに必要なさそうです。わたしの場合は良くサフィックスをつけ忘れるのでどちらかといえばハンガリアン記法はニガ手な方でした。
あとはよく言われていることなので特に問題はないと思います。
■大事なのは……
結局、コーディング規約というのは「みんなが分かりやすいコードを書く」ための知恵だといえます。私はコーディング規約を「最低限のお約束」だと認識しています。本来はほとんど必要のない項目かも知れません。でも、仕事でプログラミングする以上、「人のためにコードを書く」ことが必要だと考えています。つまり、作成するのは自分でも、使ったりメンテナンスする人は他の人なのでその人たちのために「分かりやすく良いコード」を書くことが大事だと思います。
個人的にはそれが明確になればコーディング規約は必要ないかも知れないとは思いますが、現状では最低限の規約だけはメンバーに提示することは必要だと考えています。もっとも、ダメなコーディングはソースコードレビューで直させるので、最終的には手間が掛からないだけで同じかもしれません。
次は、フリーソフト・オープンソースについてです。以前、さらっと書いた記憶はありますが、ちょっとこの2つをわたしなりに掘り下げたいと思います。
コメント
saki1208
にゃん太郎さん、こんばんは。
saki1208です。
しばらくぶりにコメントさせていただきます。
突発的に忙しくなったもので、コラムは拝見させていただいていましたが...
私もコーディング規約自体は必要だと思いますが足枷にならない程度のものであることが重要ではないかと思います。
命名規則については直感的に機能がわかるとか、変数の用途/意味がわかるというのが重要で、ハンガリアン記法のような「変数の型」がわかることはあまり重要ではないと考えます。
なぜなら、最近のプログラミングではIDEを使用して開発することが多く、マウスポインタを合わせれば型などの情報は表示することができます。また、機能とは関係ありませんし、普段使用しない型や省略形が同じになり得る型では思考が停止してしまいます。
私の作業場所では例外無くハンガリアン記法ですけどねorz
それよりも、禁止事項や推奨事項を充実させることの方が重要ではないでしょうか。
記述内容/方法によりバグの温床となってしまうものやそこまでではないけどできるだけこっちで!!みたいなものを理由や事例をもとに説明して守らせるほうがよいと思います。
どこかで書きましたけど「すべてのブロックの先頭でOn Error Resume Next」が記述されているVB(.Net前)なんて最低です。
後は、数百行のメソッドとか...
コーディング規約もプロジェクトを繰り返す度に進化していけばよいのですが厳しく縛り付けられるところほどメンテは行われない気がします。
にゃん太郎さん、こんばんは。
私はコーディング規約は必要派です。
しかし、既存のコーディング規約はひどいものが多く、それが技術の伸びを殺している。それで「変えろ!」と、ちょうど揉めているところで、飲んで暴れたところですorz。
技術者が前例主義になってどうする!
技術者が技術的検証をしないでどうする!
つってもね、サラリーマンには分かるまい……。
酔っ払いですみません。
Atsushi777
自分以外の人が保守する確率が高いというようなことも含めて、
開発する環境によるものが大きいと思うのですが、
開発メンバーの能力をどの程度以上であると前提としておけるかというのが、
きわめて重要なのだなと思いました。
はっきりいって、
> ローカル変数の使い回しはしない(ループ変数など)
こんなことを守るのはバカらしいというか、
そもそも最近はループ変数の使用頻度そのものが少なくなっていたりしますが、
こんなことをルールとして決めなくてはならないのがかわいそうでなりません。
こういっちゃ悪いですが、コーディング規約を決める人の能力が低いばかりに、
普通に書いたコード、もっと言えば保守性を考慮したためにテクニカルになっているコードを
(よくわからないから)読みにくいなどと言われるのは勘弁してほしいです。
さすがに「読みやすい = 保守しやすい」と単純に考えているとは思っておりませんが…。
にゃん太郎
saki1208さん、ありがとうございます。
「推奨事項」って言うのはあまり考えないですよね。「コーディング規約」=「やっちゃいけない事」ですから。最近のIDEはカッコ前後のスペースとかインデントとかは割合いい感じでやってくれるので昔ほど気を使わないで良いのがありがたいです。そういや、昔はソースコードを整形してくれるツールもあったっけ。
コーディング規約でバグが無くなることはないでしょうが、少なくともアホらしいバグは減ると思ってます。ですから、
> 記述内容/方法によりバグの温床となってしまうものやそこまでではないけど
> できるだけこっちで!!みたいなものを理由や事例をもとに説明して守らせる
> ほうがよいと思います。
これには賛成できます。是非、使わせて頂きます(笑)
にゃん太郎
生島さん、ありがとうございます。
> 技術者が前例主義になってどうする!
> 技術者が技術的検証をしないでどうする!
おっしゃる通りでございます(笑)
コーディング規約だけに限らないと思いますが、サラリーマンだとどうしても楽な方に流れてしまうのは制度上(?)仕方ないと思います。私の部下を見てもそう思うので。もっともあまり人の事は言えませんが…あう。
でも、今年いっぱいでサラリーマン生活にはさよならする予定ですけど、どうなることやら(苦笑)
にゃん太郎
Atsushi777さん、ありがとうございます。
> こんなことをルールとして決めなくてはならないのがかわいそうでなりません。
私はどちらかと言えばコーディングを「規約を決めてまでルール化しなくても良いんじゃない?」とは考えています。でも、好き勝手にすると統一感がなくて読みにくいコードがあちこちに出来てしまいます。エンジニアなら時間が掛かるかどうか別にして他人のコードを読むことは出来ると思いますが、読みにくい事により仕様変更時などにバグを入れ込んでしまう可能性もあります。
> 普通に書いたコード、もっと言えば保守性を考慮したためにテクニカルになっている
> コードを(よくわからないから)読みにくいなどと言われるのは勘弁してほしいです。
これもコラムで少し書きましたが「保守性を考慮したためにテクニカルになっているコード」は人によって意見が分かれるところと思います。メンバーや管理者のスキルや考え方の方が大きく、結局はPMやSEがどこまで「規約」を詰めるかになってしまいます。生産性も保守性もキーワードとして重要ですがソフトウェア開発では意外と抽象的で人によって考え方の差異が大きいと思います。それを解決する方法の1つとしてコーディング規約があるのでしょうが、奥が深くて難しい問題です。
はやしさとし
にゃん太郎さん、こんにちは。
.NET専門会社の弊社の場合ですが、ぱっと思いついた項目を書きます。
過去に経験したプロジェクトで問題になった部分です。
VB.NETとC#が混ざっている点はご容赦ください(^^)
-----------------------------------------------------------
・配列を戻り値にするメソッド
返すものがない場合、空の配列を返すのか、nullを返すのか
・string型変数の初期値
空文字を入れておくのか、nullのままでよいのか
・ループ
do whileを禁止して、whileのみで書くのか
・VB6.0時代の互換のキャスト
CInt、CLongなどを許可するか
・参照型のキャスト
DirectCast、CType、TryCastのどれに統一するか
・文字列の連結
&で連結するか、+で連結するか
・三項演算子やIIFの使用を許可するのか
・null結合演算子(null coalescing operator)の使用を許可するか
・null許容型(Nullable Type)の使用を許可するか
・Subから抜ける場合、Exit SubかReturnのどちらに統一するか
・ifの評価は、ショートサーキット評価での記述を厳密化をするか
・yield returnの使用を許可するか
・DBのコネクション
1つだけ作って開け閉めするか、毎回作って破棄するか
・IDisposable型
usingでの解放を義務づけるか、各自が責任を持ってDisposeを書くか
・usingでのエイリアスの定義の使用を許可するか
・VB.NETでのメソッドのオーバーロード
Optionalで省略可能な可能なメソッドとして書くか、改めて引数を増やしたシグネチャで書くか
・初期データ付きで静的なHashtable
プロパティとして定義し、プロパティ内で生成と初期値の設定を行うか
フィールドにして定義し、静的コンストラクタで生成と初期値の設定を行うか
-----------------------------------------------------------
失礼しました。
にゃん太郎
はやしさとしさん、ありがとうございます。
なんかいっぱいありますね(苦笑)まぁ、うちも.Net使うから良く分かりますが。
JavaやC#を使うようになって「null」と空をどうするかという事は前は結構悩みました。あまりこの辺りって明文化しないですから(私の経験、では)DBもそうかな。C/C++が長いとnullもゼロも同じ(型違い、だったっけかな。でもC++ではintだったような気がする)感覚になるので、最初はC#でマジで「null=0」と思ってた。
こういう細かい事も必要なのか、ねぇ。
はやしさとし
にゃん太郎さん、おはようございます。
目指すところは、「コードレベルのムラをなくして平均化する」ということでしょうか。
言いかえるならば「落とし所」を設定するという感じ。
若い人向けメッセージ→
新しい機能を使いたいのはわかるけど、「家に帰ってからも趣味のプログラムを組む人」ばかりじゃないから、手加減してね。
新機能といえども、実現手段の一つであって、新機能を使うことが目的じゃないからね。
ベテラン向けメッセージ→
確かにVB6.0の頃のコードでも「下位互換性」のために動かすことができるけど、新規プロジェクトでは、VB.NETの作法で書いてね。
VB6.0の頃の書き方を、新人が鵜呑みしてコピペするから、本当にやめてね。
インドリ
興味深い話題ですね。
日本のコーディング規約と米国のものとは毛色が違うと感じます。
日本のは「みんな一緒の新人用儀式」。
米国のは「プロ集団としての規約」。
これはC++ Coding Standards、Effective C++、Effective Java・・・などの書籍を見れば感じ取れると思います。
さらに大きな違いは、米国は実装デザインと論理デザインを考慮した「設計と実装を横断するプロの知識」をコーディング規約にしているのに対して、
日本では「現場を知らない管理者」や「設計を知らないプログラマ」が決めているという事が多いように感じられます。
私が今まで目にした規約は、プロとしての規約とは思えなんですよね・・・
相でない現場もあるでしょうが、コーディング規約を何か誤解している現場が多いように感じてしまいます。
にゃん太郎
はやしさとしさん、ありがとうございます。
> 言いかえるならば「落とし所」を設定するという感じ。
個人的には「そうではない」様にしたいのですが、結果的にはそうなってると思います。経験とスキルの差もありますが、例えばマイクロソフトに限って言えば都合よく売るためにコロコロ仕様を変えてくれる。その影響はベテランには今までのやり方の否定を迫り、若手には便利で面白い機能を提供して興味をそそるようにしてくれる。こうなるとコードの書き方や考え方がバラバラになってしまう。管理する側からするとどっちの言い分も理解できますが、それじゃ進まないので「規約」という体裁で落とし所を作ってるとは正直思います。
にゃん太郎
インドリさん、ありがとうございます。
もともと日本ではコーディング技術や考え方に関しては教育しないのでそういう意味では未熟だと思っています。出来上がった「もの」がすべてなので(これに関しては間違いではないのですが…)「コーディング規約」が統一感を出すための指針と言う性格になってしまいます。本来はパフォーマンスも考慮したものなのですが。
ソフトウェアのビジネスモデルと言うのはSI以外では(携帯電話みたいな組み込みも含めて)開発→リリース→バージョンアップ作業→リリース…の繰り返しとなります。日本ではこのサイクルが早いのでとにかく商売上早くリリースする事を求められます。ですから設計も実装もとにかく「作って動作させる」事が優先なため、コーディング技術に関してはやや置き去りでも完成さえすれば黙認されます。この黙認の歴史が現在の状況なんでしょうね。
saki1208
にゃん太郎さん、こんばんは。
saki1208です。
> 「推奨事項」って言うのはあまり考えないですよね。「コーディング規約」=
>「やっちゃいけない事」ですから。
>> 記述内容/方法によりバグの温床となってしまうものやそこまでではないけど
>> できるだけこっちで!!
引用していただいた私のコメント内の前者が禁止事項、後者が推奨事項の意味で書いてました。わかりにくくてすいません。
にゃん太郎
Jittaさん、ありがとうございます。
> ハンガリアン記法は、積極的に使います。使わないなんて、勿体ない!
> まして、使うななんて、信じられない。
いや、信じて下さい(笑)ま、個人なら好みで良いと思いますし、職場ならそのガイドラインに従えば良いと思います。これが良いのか悪いのかは各々職場なりで議論して頂ければ。私のチームでは「使いません」と言うだけです。まぁ、基本がオブジェクトなので接頭語が面倒くさそうなだけだと思うけど。
# そういや、うちの職場の若い子が「string」が変数だと思っていたらしい。おいおい
はじめまして。ようすけと申します。
仕事の合間にざっとしか読んでいないのですが、(あとで全部拝見します!)
同感してしまう部分が多々あり、思わずコメントしてしまいました。
私はフリーランスのPG・SEで経験としてはまだ10年も満たない者です。
これまで携わった開発現場はなぜかコーディング規約が存在しない現場が多く、私の感覚ではどうしても納得できない(読みにくい)コーディングによく出くわしました。逆もしかりで、わかりやすい(読みやすい?)コーディングに出会うこともあります。
と、勝手に人のコーディングを自分なりに評価していますが、私は自分ののコーディングを人に評価してもらったことがなく、なかなか自信を持てずにいます。
経験をつんでいけば自信を持てるのでしょうか?
駄文失礼いたしました。
にゃん太郎
ようすけさん、ありがとうございます。
> 経験をつんでいけば自信を持てるのでしょうか?
コーディング規約ってそもそも他人のために「こうやってコーディングしましょう」って言うものですから、職場毎に考え方はあると思います(それの良し悪しは別として)評価にしても評価する人の基準が統一されないでしょうから結局、自信は己で納得する以外ないと思います。そういう意味では経験を積むのが一番ですが、個人的にはコードレビューしてもらうのがいいかと。ダメなときは「なぜダメなのか?」を明確にしてもらいます。単純に「良くない」と言われた時はその人にとって気に入らないだけですから無視して構いません。私は「ダメ」な理由は明確にします。逆に明確にならない時は言いませんが。
日々精進が一番ですよ。頑張ってください。
Jitta
> まぁ、基本がオブジェクトなので接頭語が面倒くさそうなだけだと思うけど。
はぁ。。。やはり、「誤解」の方が広まっているんですね...
にゃん太郎さんはじめ、ここで「ハンガリアンいらね」派の方々は、「型によるハンガリアン」を使っていらっしゃるのだと思われます。確かに、それは、要らなくなりました。コンパイラが型を厳密にチェックするようになったし、オブジェクト指向言語ではまず意味を持たないでしょう。
しかし、私が言っているのは、「意味によるハンガリアン」です。これを、「アプリケーション ハンガリアン」と言います。
http://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%B3%E3%82%AC%E3%83%AA%E3%82%A2%E3%83%B3%E8%A8%98%E6%B3%95
『
本来、シモニーの考案したハンガリアン記法とは、変数の意味や使用目的から接頭辞を決定することであり、型では区別できない情報を変数名に付与することで、紛らわしい変数の意味を明白にし混同をさけるためのものであった。たとえば、論理座標とデバイス座標、X軸とY軸、ドルと円などで、これらは単純に型による安全性に頼ることはできない。
』
例えば、「ユーザーが入力したままの文字列」、「SQL 用にエスケープした文字列」、「HTML 用にエスケープした文字列」、「データベースから持ってきたままの文字列」、「Shift_JIS な文字列」、「EUC_JP な文字列」などを、区別することは必要だと思われませんか?これらは全て「文字列」であり、型によって区別する必要はない、区別することは出来ません。しかし、データベースへ引き渡すのに、「ユーザーが入力したままの文字列」を渡していれば、それは危険です。コンパイラも検出してくれませんし、テストでも適切なデータを用意していなければ検出できません。そういうミスを、コードを書いている段階で防ぐことが出来る可能性を引き上げます。
みりゅ
つれづれに思うことのつづきです
スパム防止のため非公開とでたので、前文が掲載できたか不明です。
----
・書き方の規則性 について
書き方にルールを与えることで、書き方の違反をチェックする
ようなツールがあります。
ビルド環境が支援する型チェックやコード・チェックが使えます。
コンパイラは、変数の型変換(キャスト)は常に正しいという前提ですが
"lint"などの静的解析ツールでは警告になる場合があります。
また、バッファー・オーバーフロー、変数のオーバーフロー、0割り算
などもツールで検出できる場合もあります。すべて検出できるわけ
ではありません。ただ、決まった書き方をして条件を狭めれば、そのような
不具合を自動で検出する効果を高められる場合もあります。
"CCCC"などを使用すれば、各モジュールのマッケーブの複雑度など
ソフトウェアのメトリクス(尺度)も取れます。コードを計量化できれば
ルールとしては、複雑度は20以下にするという風に書けると思います。
いわゆる、コピペ・コードも、コード・クローン検出ツールなどで
検査し直していけます。
インデントはそろえておこきたいです。
インデントが4文字のソースコードの中に8文字の
コードが混じっていると、見た瞬間は、分かりにくいと
も思えます。
そこにネスト(入れ子)の動作が判読できないとき、単に理解が
足りないのだろうけど、間違った理解なのかで混乱します。
インデントは個人的にはtab は 4文字が好きですが、
条件で変わるtab文字より tab文字を使わず空白文字が尚更好きです。
ただ、空白文字は4文字にしたいところが3文字になったり
間違いやすいので趣味としての意見です。
また、演算子などの優先順位のについても、
「よく分かっている人」と「わかって無い人」が混じっていると、
「他人」からみると、明らかにレベルの低い記述である部分は
不快を感じるだけですが、括弧('('と ')')の無い部分は読むのに
難儀します(「仕様」とよく突き合せなくてはならないため)。
----
・命名規則
よくソースコードを読めといいますが、
個人的にはコード自体は結局に処理の手続き順序でしかないと
考えています。
重要なのは、どのようなデータが入力されて、どのように加工されて、
どんな結果がでれば良いのかだと思います。
それを分かりやすくするのがデータの構造と名前、名前空間やスコープ
の問題だと感じます。
型(タイプ)についてですが、C言語では、これはむしろ、
きっちり宣言をすることや、ヘッダーを適用範囲や境界(ドメイン)を
意識して作ることなどで重複しない形に参照しやすくすることで回避
するとか。不要なヘッダを参照していないなど、調査の範囲から
限定しやすくすることです。
例えば、画像を加工するソフトを考えるとき、3つの部位
(ファイル、データ加工、表示)があったとして、テキストの検索で
10000行中に200箇所もデータ転送に関するTransfer()という記述があったら
げんなり するでしょう。
ただ、3つの部分でやり取りされるデータは、同じ構造の
image_format(固定長)と image_data(可変長)のように1つに
定義できるでしょう。
また、それぞれが、File.Transfer(), Processe.Transfer(),
Display.Transfer() であったら共通の機能 Transfer() の定義は
1箇所でしょう。3つの部位のどれかを検索する場合には、
その内の 1箇所を調べればいいよとなりましょう。
他には、変数 xxxx_100msは、100ms毎に内容が変更されることを
期待したいとします。
これが、実際には1000ms毎で変更されていたら怪しいと思いますよね。
もっとも、最初から完璧な命名にこだわる必要も無く、それぞれの
名詞に同じものがない・固有であれば、まずは良いようにも思えます。
そこに、目的や、意味などから、きっちりしたものへの変更は、
テキスト・エディターなどでも簡単に一括変更できるから、
最終的にはまとめる方向は持たせたいです。
そうすれば、名前は索引のようにも機能します。
コール・グラフをとればモジュールの参照順序が分かるので
循環参照しているなど、良くない部分や同じような機能の階層
が揃っているかも判断できるでしょう。
同じような名前を含むものが、違う境界(ドメイン)の同じ階層に
あれば内容の把握を手助けするでしょう。
Doxygenなどのソースコードのコメントからドキュメントを作成
するようなツールでは、テストコードやサンプルとのリンクを貼る
ことで簡単な関数仕様書みたいなものも作成できます。
また、変数の索引も生成できるので、それを見て似たような内容を
扱う変数たちの、日本語のコメントが ばらばら だったら直そうと
思うでしょう。
ソースコードをツールに掛けることで名前やコメントが
抽出できたり加工できるということは、
名前やコメントも「リファクタリング」の対象であるともいえます。
コメントが ちぐはぐ だったり、間違っていると、
間違った理解を誘発して苦労することになります。
みりゅ
長くて申し訳ありません
こんどはXSS攻撃と間違われたようで、書き込みに苦労しました。
(半角中括弧がNGワードなのね?)
---
・ if / else
if (flag1 == a1)
{
/* 処理1 */
}
/* else はない */
if (flag2 == a2)
{
/* 処理2 */
}
else if (flag3 == a3)
{
/* 処理3 */
}
else if (flag4 == a4)
{
/* 処理4 */
}
/* else はない */
と、いうようなときを考えてみます。
ここで、処理条件が変更になった。その正しいコードはこう。
if (flag1 == a1)
{
/* 処理1 */
}
else if (flag3 == a3)
{
/* 処理3 */
}
/* else はない */
しかし、このソースを触っている別の人がいたとしましょう。
このソースは正しいのだけれど、変更した当人と話していない場合、
間違って処理を消してしまったかのか判断が難しいと思うかもしれません。
ただ、/* else はない */ の消し方で、
処理2と処理4は意図的に消したかなとは思えるかもしれません。
かといって、変更した当人と話していても、当人は正しいと
主張するかもしません。
まぁ、テスト仕様書やテストコードがあって「パス」していたら
問題ないでしょう? 結果の少なくとも再現できます。
元のコードでは、少なくとも処理1.5だとか処理5は無いのは
分かりますよね。
::: 「絶対に通らない処理は作らない」
これは自分も賛成です。理由としては「他人」から見るとそのコード
に遷移する条件が「あるように」見えるからです。
さて、先の例の元のコード 処理1~4がある状態を示す値を返す判定
をするものだとします。処理1~4を通らない場合の状態の値は0 です。
ただし、このとき、この上の部分で「状態の値が-1」を出していたと
しましょう。つまり、上の部分に「不具合があった」と。
ここで /* else はない */の部分で「念ために」と else を
つけて、「状態の値 0」としていたらどうでしょうか。
「勝手に」に「バグが正常」となってしまう「爆弾」に変わります。
本来あってはならない、状態の値が-1 が検出できなくなっている
(まぁ、単体テストをしっかりやっていれば検出できるでしょうが)。
ようは、不要なコードは品質を低下させるということ。
もし、「念のため」というのなら、"assert文"(主張)で来ないはずの
ものがきた場合は、異常通知してもらう方が、デバッグやすい訳です
(最終状態で、assertが実行されては困るけど)。
コードで値や状態がどのように変化していくか把握しておきたい訳です。
----
・ goto
個人的には、共通のエラー処理などとしてのgotoは自分も使います。
理由は、Linux Kernel のコーディング・スタイルと同じ。
----
・関数の終点
関数の出口(return)は一箇所にする。
これは、デバッガーでブレークポイントを貼るときに1つの出口に
1つ貼るだけでよいからです。
出口が複数あると、出口の数だけブレークポイントを貼ることになります。
また、貼り忘れると いつの間にか関数から抜け出していて
テストし直しという手間が増えますね。
さらには、「いつの間にか抜け出していて」いるようなものは
不具合との判定もできるということです。
----
NULL(ナル/ヌル)
C/C++言語の「仕様上の定義」ではNULLはNULLという独立した意味です。
NULL = 0 ではありません。
多くのコンパイラやライブラリで「実装上」そうなっているだけです。
「実装上」そうなっているので、0で扱わないとうまく動作しない
場合もあるようです。
なお、ASCII文字セットのNULL文字は、エンコード 0x0 です。
みりゅ
> 設計も実装もとにかく「作って動作させる」事が優先なため、
> コーディング技術に関してはやや置き去りでも完成さえすれば黙認されます。
欧州の自動車業界にはMISRAというガイドラインがあるけれど、
日本においてはIPAなどがガイドラインの検討をしているようです。
C言語のコーディング作法(ver 1.0)
http://sec.ipa.go.jp/download/dl.php?filename=report/200606/CMGuide_V1-0.pdf
sumeshi
> if(flag == true)
booleanの使い方・名前のつけ方がわかってない人の発想で、「フラグ」という概念から抜けだしてないです。「フラグ」だとtrueが何でfalseが何を表してるかわからないです。
「enabled」「exist」「isなんとか」等、見たことあると思います。こういう名前をすんなり受け入れられるようになると if (exist == true) { って野暮ったく感じませんか? if (a == 1 == true) { って書いたら変ですよね?それと同じレベルということです。
規約関係でまだ話に出てないようですが、変数の宣言を全部メソッドの上に書く人がいるけど、あれ読みにくい(いつからどういう値が入っているか分かりにくい)し、使わないのに宣言したり、使いまわしたりの原因となります。変数のスコープが最小になるように書くことを規約にいれたほうがいいと思ってます。
間違った記述が、さも正しいように書かれていたので突っ込みます。
> NULL(ナル/ヌル)
> C/C++言語の「仕様上の定義」ではNULLはNULLという独立した意味です。
> NULL = 0 ではありません。
> 多くのコンパイラやライブラリで「実装上」そうなっているだけです。
「NULL == 0 ではない」の記述ミスだと思いますが・・・
NULL == 0 ではない?
仕様書を読みましたか?
C の仕様書には次の記述があります。
The macro NULL is defined in (and other headers) as a null pointer constant
では null pointer constant の定義はというと
An integer constant expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant.57) If a null pointer constant is converted to a
pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal
to a pointer to any object or function.
したがって NULL == 0 というのは「仕様上正しい」
では C++ ではどうか?
NULL がマクロで、null pointer constant であるという定義は C と同じ。
C++ における null pointer constant の定義はというと
A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero
or an rvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result
is the null pointer value of that type and is distinguishable from every other value of pointer to object or
pointer to function type. Two null pointer values of the same type shall compare equal. The conversion of
a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a
pointer conversion followed by a qualification conversion (4.4). A null pointer constant of integral type can
be converted to an rvalue of type std::nullptr_t.
したがって、C++ でも NULL == 0 は間違いというわけではありません。
次に「NULL 文字」
「NULL 文字」というものは C/C++ の仕様上存在しません。
大文字で "NULL" と表記した場合は、上記定義が存在するため、
null pointer constant を意味することになります。
ASCII 文字セットの NUL 文字は、C および C++ どちらの仕様書でも
null character として定義されています。
( なお C, C++ どちらの仕様書でも NUL とあった場合には
ASCII character set のヌル文字を指します。)
-------
このエントリの本題の「コーディング規約」については、
わたしは最低限の規約は必要だと思っています。
「最低限」の線をどこに引くかは、言語によって、また環境によっても異なると思います。
C++ の場合は、次の規約は最低限必要ではないでしょうか?
* コンストラクタ・デストラクタの中で例外が発生するコードは書かない
* goto は使わないようにしましょう
* グローバル変数は使わないようにしましょう
* 使い終わったリソースは解放しましょう
* 非公開 API は使わないようにしましょう
* obsolete がついたものは使わないようにしましょう
ぱっと思いついたものはこれくらいしかないのですが・・・
コーディング規約は読みやすく、メンテしやすいコードを書くためのもの
という考えはわたしもその通りだと思います。
開発メンバー全員がこの心がけを持っていれば、規約は少なくなると思います。
その逆の環境ではコーディング規約は増えると思います。
ハニー
> プログラマの仕事は、究極的には設計書通りにバグなしで早く動作する
>(ストレスがない動作をする)プログラムをコーディングすることだといえます。
この人、何バカなこと言ってるの?(*゚▽゚*)
設計書どうりに作ったらバグだらけだよ。
実力が十分ならバグのないプログラムを作るのは可能だけど
むしろ、バグの無い設計書を作るのがどれだけ大変かわかってないんじゃないの?
プログラムより設計書の方が難易度が高いから、設計書作る人のが給料高いのだよ?