かつては“常識”だったのに,今ではそうでなくなっている――ソフトウエア開発の世界では,そんな知識や習慣がめずらしくない。10年以上のキャリアを持つ開発者の方なら,思い当たるものがきっとあるはずだ。

 例えば,昔は「すでに動作しているコードに変更を加えてはいけない」というのが常識だった。汚いコードに手を入れようとして先輩にしかられた経験のある方もいらっしゃるだろう。ところが今では,「リファクタリング」の名のもとに将来問題が発生しそうなコードを修正する作業がむしろ推奨されている。EclipseやJBuilderなどのリファクタリング支援機能を搭載した開発環境も増えており,今やリファクタリングは完全に市民権を得たと言ってよいだろう。

オブジェクト指向技術の浸透,変化が大きな要因

 もちろん,常識が変わった背景にはオブジェクト指向開発の浸透といった開発手法の変化があることは間違いない。あらかじめアプリケーションをクラスなどの単位で適切に切り分けておくことで,変更が広い範囲に波及するのを防ぐことができるからだ。加えて,安全にコードを変更するための手順がある程度確立されたこともある。以前はコードのクリーニングによるメリットよりも,バグが混入する危険があるというデメリットのほうが大きかったのが,危険性が少なくなることでメリットのほうが大きくなったというわけだ。

 オブジェクト指向開発におけるクラス継承の位置付けも,昔と今では開発者の間での認識が少し異なる。昔は,継承と言えばまず実装(コード)の再利用を思い浮かべる人が多かったはずだ。確かに,オブジェクト指向になじみがない人にとって,実装の再利用が最も分かりやすいメリットなのは間違いない。しかし,現在では実装の継承よりも,多態性を利用するためのインタフェースの継承のほうが重要だという認識が広まっている。「継承は多態性のための手段に過ぎない」とまで言われることもあるほどだ。

 理由の一つは,継承による再利用に耐えられるクラスを作ることが非常に難しいことである。現在開発中のアプリケーションの機能を基底クラスと派生クラスに分割して実装する場合はともかく,将来どのような形で利用されるかをあらかじめ予測してクラスを設計することは,経験豊富なオブジェクト指向開発者にとっても非常に難しい作業である。結果として,ベンダーがライブラリとして提供しているクラスを継承することはあっても,過去に自分や同僚が作成したクラスをそのまま継承できるケースは少なくなる。

 加えて,クラス継承には「脆弱な基底クラス」(fragile base class)と呼ばれる問題がある。基底クラスのあるメソッドfが内部で別のメソッドgを呼び出している場合に,派生クラスでgの振る舞いを変更(オーバーライド)するとfの振る舞いまで変わってしまう,というのがその例だ。こうした問題が発生するかどうかは基底クラスにおけるfの実装に依存するため,クラスを継承する際に基底クラスをブラックボックスとして扱うことができない。JDKのクラス・ライブラリのリファレンスに「This implementation・・・」というフレーズが随所に現れるのもこれが大きな理由である。

 実は,実装の継承よりもインタフェースの継承のほうがより重要である,というのはずいぶん前から言われていたことだ。実際,米Microsoftが提唱したオブジェクト間の通信規約であるCOM(Component Object Model)もこの考え方に基づいて設計されている。COMを利用してオブジェクト指向技術を一部導入したVisual Basic 5.0/6.0が,実装ではなくインタフェースの継承をサポートしているのもこのためである。

 にもかかわらず,Visual Basicのオブジェクト指向機能が十分でないと言うときに真っ先に取り上げられるのは,実装の継承ができないことだった。実装の継承よりもインタフェースの継承のほうが重要である,という認識が広まったのは,オブジェクト指向開発の経験がある程度蓄積されたこともあるが,Javaが実装の継承とインタフェースの継承を言語仕様として区別したことが大きいのではないだろうか。

コーディングのレベルでも“常識”は変わっていく

 コーディングのレベルで見ても,時代によって良い悪いの判断が異なるものがいくつかある。

 例えば,昔はC言語でプログラムを組む際に先頭に長々とマクロ(プリプロセサ命令)を記述していたものである。うまいマクロが組めることが上級者の証であり,マクロの組み方で年季が分かる,とまで言われたものだ。ところが今では,型チェックが働かない,予期せぬ副作用が発生する,などの理由でマクロは完全に悪者扱いである。C++の教科書の多くには,マクロの代わりにconst修飾子による定数定義やインライン関数を使え,と書いてあるし,Javaはそもそもマクロを用意していない。

 もちろん,昔のCプログラマがこうした問題を認識していなかったわけではない。C言語で定数に名前を付けるにはマクロ(#define)を使うしかなかったのである。対して,C++ではconst修飾子を付加した変数が定数として振る舞うように変更されたため,配列を宣言する際の要素数などをconst変数を使って指定できる。言語の進歩によって必要悪が単なる悪に変わった格好だ。

 ハンガリー記法も,昔と今で評価が異なるものの一つだ。ハンガリー記法は,変数名の先頭に変数の型などを表す数文字を付加するという変数の命名規則である。例えば,ファイルのサイズを表すDWORD(符号無し32ビット整数)型の変数ならdwFileSizeのように,型を表す“dw”などのプレフィックスを変数名の先頭に付加するのである。これに加えて,クラスのメンバー変数なら“m_”といった変数のスコープ(適用範囲)を表すプレフィックスを付加することもある。

 Windowsアプリケーションの開発が本格的に始まった10年ほど前,ハンガリー記法はC言語によるWindowsアプリケーション開発における“標準的な”命名法であった。MS-DOSなどから移ってきたCプログラマの多くは,「呪文みたいだ」「美しくない」などと感じながらも,「郷に入れば郷に従え」とばかりにハンガリー記法を採用したものだ。型のような本質的でない情報を変数名に含めるべきでない,という批判もあったものの,その多くはWindows以外の開発者によるものだった。

 だが最近は,こうした批判の声がWindows開発者からも聞こえてくるようになっている。ちなみに,マイクロソフトのVisual C++ 6.0は,クラス・ウィザードなどでメンバー変数を作成する際にデフォルトで“m_”を付加するが,Visual C++ .NET以降では廃止されている。

 ハンガリー記法の利用者が減りつつあるのはなぜだろうか。一つには,以前ほど必要ではなくなったことが挙げられる。Windows 3.0以前のSDK(Software Development Kit)では各種のハンドルがすべて同じ型であったうえにポインタとしてそのままアクセスできたため,混同しないようにプレフィックスを付加することに大きな意味があったのだ。だが今ではハンドルは種類ごとに違う型として定義されているし,C++で開発するならそもそもハンドルを直接扱うこと自体,めったにない。結果,変数の型を変更するたびに名前まで変えないといけない,といったハンガリー記法のデメリットのほうが気になるようになったというわけである。

 もっとも,記者自身はこのほかに,Javaによる開発が広まったおかげでJavaの命名規則が浸透したことも大きいのではないかと考えている。Linuxやオープンソースの台頭によってUNIX系開発者の声が大きくなった影響も無視できないだろう。実際,.NET Frameworkのクラスライブラリの命名規則はJavaとほぼ同じである。

たまには立ち止まって自分の常識を疑ってみよう

 長い間当たり前だと思っていたことがいつのまにか当たり前でなくなっている,というのは何となく腑に落ちない気がするのも事実。ただ,ここまで挙げてきた例を眺めてみると,多くは元々メリット/デメリットの両方を持っていたことが分かる。

 プログラミング言語の進歩や開発手法の変化などによってそのバランスが変化すれば,常識が変わるのも当然だ。開発チーム全体の慣れやスキル・レベルによっても,どうするべきかは変わるかもしれない。変数の命名規則など,あまり本質的でないものはプロジェクトの規約に従えばよい,と言ってしまえばそれまでだが,たまには自分の常識を疑ってみることも必要ではないだろうか。

(山本 哲史=日経ソフトウエア)