第7章 コーディング標準と規約 - 『Code Reading』

1、2年前、「技術書を読みながら考えたことを、クローズドなTwitterアカウントで延々つぶやく」という根暗なことを定期的にやっていました。ふと、あれをブログでやるとどんな感じになるんだろうと思ったので、実験的にやってみます。

Code Reading―オープンソースから学ぶソフトウェア開発技法』の第7章「コーディング標準と規約」を読んで、考えたことや分からないことをダラダラと書きます。本の内容のまとめではありません。

まとめではないといいつつ、7章の冒頭で挙げられている有名な3つのコーディング規約に、一応リンクしておきます。

7.1 ファイルの名前と編成

Javaでは、クラス変数、インスタンス変数、コンストラクタ、メソッドの順に書くのがよいと書かれています。たしかに、出典が、「3.1.3 Class and Interface Declarations」にありました。

しかし、変数とメソッドをprivate、protected、publicの順で定義せよ、とも書かれており、こちらの出典が分かりません。逆では?

11.1 Java Source File Example」を見るかぎり、すくなくとも変数の定義は逆です。このコードには、メソッドはpublicしかありません。

個人的には、Javaの書き方は、次の本をベースにすればよいと思っています。

Javaルールブック ?読みやすく効率的なコードの原則

Javaルールブック ?読みやすく効率的なコードの原則

練習問題 7.2

厳密に指定されている順序でコード要素を定義して宣言することの長所と短所を論じなさい。最近の統合開発環境が読者の見方にどのような影響を与えていると思うかを述べなさい。

この話題は、ファイル内のメソッド定義位置が、同じファイルからの呼び出し可否に影響を与える言語か、そうでないかによって、考えるべきことが変わってくると思います。…が、今回は考察していません。

  • 長所
    • publicメソッドがファイルの上か下にまとまっていれば、その部分だけをざっくり読むことがラクにできる。
  • 短所
    • 新規にメソッドを書く時、挿入箇所に注意を払わなければならず、煩わしい。
    • また、可視範囲ではなく、振る舞いの種類ごとに関連メソッド群をまとめたい場合に、困る。

IDEによる影響としては、たとえば、Eclipseを使うと、コード要素を指定順序に並べるための手間を減らすことができます。

「表7.1 一般的なファイル名」について

このような個別のファイル拡張子を把握するのも大事ですし、使用しているツール特有のファイル/ディレクトリ構造を知っておくのも大事だと思います。フレームワークやビルドツールによって、お約束の構造があり、どこに何があるかを把握できていれば、コードを読みやすくなるハズです。

7.2 インデント

インデントの話題では、ハードタブ、ソフトタブという言葉がよく出てくるので、意味をおさえておきます。

ハードタブ
タブ文字「\t」を使ったインデント
ソフトタブ
半角スペースを使ったインデント

ハードタブとソフトタブの使い方については、こういう話があるようです。

ハードタブ(viでは ts)は一般に8文字ぶんの幅だが、ソフトタブはその補助として定義され(vi では sw)、通常4文字幅とされている。
ハードタブはブロックを示すのに使われ、ソフトタブは文が1行に収まらずに折り返さなければならないときに使われる。

字下げスタイル - Wikipedia

さて、インデントをどう書くかという話はさておき、コードリーディングの観点で考えれば、次の2つができればよいでしょう。

  1. 対象のコードベースで使われているタブ設定を正しく把握すること
  2. その設定を手元のエディタに反映させること

本文では、コメントに書式設定が書かれている例が挙げられています。エディタによっては、この設定を読み取り、表示を自動的に調整してくれるそうです。素敵ですね。(!!!未確認!!!)

書かれていない場合は、どのように判別するのでしょうか。その方法が練習問題 7.3で問われていますが、自分にはすぐに思いつきません。まともにやろうとすると、文字コードの対応も必要で難しそうです。

色々見て回っていたら、コードを公開されている人がいたので、メモしておきます。

7.3 書式

7.3節は、スペースや中括弧、コメントの使い方について書かれています。

書式は言語やチームによって様々ですが、基本方針として、

  • 書くときは、コード全体で統一することが何よりも大事
  • 読むときは、どんなスタイルでも読めることが大事

という価値観を繰り返し教わったので、この手の話は、いつもそういった結論ありきで読んでしまいます。刷り込みこわい。

コメントは人間だけでなく機械(ツール)も読むものである、という話は、そういえばそうだなと思いました。ツールというのは、Javadocやlintなどです。

コンパイラ警告抑制のためのコメント

プログラムの制御フローが到達しないポイントを知らせるために、

 /* NOTREACHED */

と書く件について、本文の説明が足りない気がしたので、分かりやすいと思った説明を引用します。

gcc では(中略)プログラムの流れが複雑に分岐していても、それの流れを辿って、すべての場合に変数が正しくセットされるかどうかをチェックしているのである。この時、基本的に関数は戻って来るものとして処理される。呼び出したらそこに戻らずに、どこかに行ってしまう関数は変態であり、そういうライブラリ関数は数少ないが、しかし、そのようなライブラリ関数をラップしたユーザ関数は、任意に作れるからである(warning() なんて良い例だが...)。この時、「ユーザ関数 warning() は戻って来ない」ことをコンパイラに伝えてやれないと、フロー解析では戻って来ることを前提にするので、無意味に「初期値がセットされていない」という警告が発生することがある。だから、コメントのかたちで /* NOTREACHED */ と書いてやると、コンパイラはこのコメントを認識し、このコメントには制御が移らないことを理解する。

Super Technique 講座〜longjmpと例外

エラーを抑制するだけでなく、正しい動きを保証したい場合は、何かassert文を使える書き方にするとよいのかもしれません。(もちろん、表明の機構が言語仕様に存在する前提になりますが)

エディタでタグとして扱うためのコメント

修正対象に「FIXME」と書いたり、拡張予定に「TODO」と書いたりする習慣について。

Eclipseの場合、[Preferences]→[Java]→[Compiler]→[Task Tags]で、好きなタグを設定できますよね。

7.4 名前付け規則

7.4節は、変数名のキャメルケースとアンダースコア区切りの話や、ハンガリアン記法の話です。

そういえば、先日、あるエントリのおかげで、ハンガリアン記法に2種類あることを知りました。本で取り上げられているのは、システムハンガリアンの方ですね。

練習問題 7.5

ハンガリアン記法による名前付け規則は、型チェックコンパイラが行っているのと同様の機能を果たすのだという主張があるが、これについて論じなさい。

(そんな主張初めて聞いたぞ・・・)

まず、ハンガリアン記法は、「名前にヒントを与えることで読む人に役割を推測させる」以上の貢献をするのでしょうか。自分はしないと考えています。なので、もしかしたら、すでにここで認識不足かもしれません。そう断った上で、、、

機械的なチェック機構を持つのでない限り、「コンパイラと同様の機能を果たす」とはいえません。慣習と異なる用法でシステムハンガリアン記法が使われた場合、つまり、型構築識別子が異なる場合に、警告は行われるのか? もし行われたとして、その警告に意味はあるのか? という話になると思います。

というか、ハンガリアン記法の話は荒れるから飲み会でやれってじっちゃが言ってた。

7.5 プログラミング作法

移植性を語るときは、どのレイヤーの話をしているかを意識した方が良いようです。プロセッサー間での移植性なのか、OS間での移植性なのか、など。

本文では、プログラミング作法の例として、「コマンドラインオプションを処理するためにargvの配列を走査するのではなく、getoptを使う」という規約が挙げられていました。これにしたがうと、記述に一貫性が出て、分かりやすくなるという話です。

しかし、例外もあるでしょう。getoptは、UNIX用のヘッダファイルに含まれています。そのため、プラットフォーム独立のコードを書こうとすると、かならずしも上の規約には沿えません。

  • 規約が暗黙の前提としていること
  • 書かれているアプリケーションの性質

の2つを把握しておくとよいと思います。

7.6 プロセス標準

7.6節は、ドキュメント作成、リリース、ビルドなどの話です。

標準化されたビルドプロセスで記述の複雑さがどうして違ってくるのかは、NetBSDのネットワークタイムプロトコル(NTP)ライブラリの64個のファイルをコンパイルするのに使われる18行のMakefileと、apacheサポートライブラリの12個のファイルをコンパイルするのに使われる、手製の40行と自動生成される37行とから成るMakefileテンプレートを比較してみるとわかるでしょう。

とのことなので、比較してみました。

netbsd/lib/libntp/Makefile
  • LIBに、生成するライブラリ名を指定
  • SRCSに、必要なソースファイルを1個1個フラットに指定
  • CPPFLAGSに、コンパイラオプションを指定
  • 共有ライブラリを作るためのbsd.lib.mkをincludeする

…これだけ。

apache/src/ap/Makefile.tmpl
  • 上に加えて、複数のビルドタスクが定義されている。cleanとか、dist cleanとか。
  • さらに、「DO NOT REMOBE」なる注意の後に、オブジェクトファイルを一旦作成するための記述が延々とある。

結論として、「記述の複雑さがどうして違ってくるのか」は、やりたいことの複雑さが違うからじゃないの、という当たり前の答えにしか至らなかったのですが、それでいいんですかね。よく分かりません。

・・・

とまあ、こんな感じで、実験終了です。誰得でしたね。。。