多くの若きプログラマたちが学ぶべきこと

私はこの7年半、Ronimoでプログラミングを学ぶ多くのインターン生を指導し、様々なタイプの大学生や大学院生を見てきました。彼らのほとんどには、共通して言える学ぶべきことがあります。特別なテクニック、アルゴリズム、数学、あるいは特定の形式についての話だと思う人もいるかもしれません。もちろんそれも必要ですが、中心的なものではないと私は考えます。彼らが主軸として学ぶ必要があるのは、自己統制力です。常に可能な限り読みやすいコードを書き、開発中の変更により秩序がなくなってきた時にはきちんとリファクタリングを行い、使用されていないコードを除去し、コメントを追加することができるという力です。

プログラミングのインターン生を指導する際、この話にほとんどの時間をかけます。上級のテクニックでもなければエンジンの詳細についてでもなく、概ね彼らにより良いコードを書かせることに主眼を置きます。いつもインターン志望者に対して、良いプログラマになるために重要だと思うことを尋ねると、大抵の場合彼らは、コードの読みやすさ、分かりやすさ、保守しやすさを挙げます。これこそが私が聞きたい言葉ですが、実際にこれを常にやり続けられる人は、経験の浅いプログラマではめったにいません。

このことを心に留めておくには自己統制力が必要です。“うまく機能している”だけでは終わらないことを意味するからです。あらゆる変数が間違った名前を付けられていても、コードは正しく機能します。ただ、コードは非常に分かりづらいものになっているでしょう。機能するコードから読みやすいコードへの一歩を踏み出す労力は、短期的にはほとんど報われることはありません。なぜならコードは既に機能していて、読みやすく変更した後でも変わらず機能するからです。だからこそ、この一歩を踏み出すには自己統制力が必要なのです。そしてそれにはインターンシップの経験が役立ちます。良い指導者はコード品質(”良いコード”の定義はプログラマごとに当然異なりますが)に対して常に注意を払っていますし、インターン生や若手のプログラマにも、読みやすいコードを書く一歩を踏み出させます。

駆け出しのプログラマが書いたコードで、私がよく目にすることを例に挙げます。

ウソつき関数/変数/クラス

名前が示唆する以外の働きをする関数、クラス、変数のことを言います。名前がウソをついているのです。名前が正しく付けられるべきなのは至極当然ですが、驚くことに、全く違う名前が付けられていることはかなりよくあります。

以前インターン生が書いたコードで私が最近見つけたのは、EditorGUIとEditorObjectCreatorGUIという2つのクラスに関してです。エディタのインターフェースを扱うコードです。驚いたことに、新しいオブジェクトを生成するボタンを操作するコードはEditorGUIにあり、EditorObjectCreatorGUIはさまざまなオブジェクトをナビゲートするだけでした。名前が示すものと、振る舞いが全く逆なのです。コードは比較的シンプルでしたが、理解するのに時間がかかりました。クラスの名前に従って、完全に誤った推測から理解を始めたからです。この場合の解決策は非常に単純です。EditorObjectCreatorGUIというクラス名をEditorObjectNavigatiorGUIに変更するだけで十分で、はるかに分かりやすくなります。

単純な名前の間違いはよく見かけます。おそらく作業しているうちにコードが進化するため、頻繁に起こるのでしょう。名前が付けられた時点では正しかったものが、コードが完成する頃には間違ったものになっているのです。コツは常に名前を心に留めておくことです。追加しようとしている機能が、その関数やクラスの名前に適合するかどうかを常に考える必要があります。

無秩序なクラス

もう1つの問題は秩序がないクラスです。関連性のない多くのことを行うクラスをよく見かけます。上で述べたのと同様に、これも同じコードに取り組み続けていると起こります。新しい機能が一番手近な場所に追加され続け、無関係の振る舞いが重なり、ある時点でクラスが肥大化してしまっているのです。また肥大化は、クラスのサイズだけとは限りません。クラス内のコードが数百行であったとしても、そこに属さないコードも含むことがあるからです。

これが起こる例として、何らかの理由でGUIクラスが利用可能なテクスチャを解析する場合を挙げます(テクスチャを選択するボタンがあるような場合です)。GUIクラスが、分析の結果を必要とする唯一のクラスなら、GUIクラス内で処理をするのは理にかなっています。しかしながら、その後何らかの理由で、まったく関係のないGameplayクラスが同じ情報を必要とするかもしれません。その場合、テクスチャの情報を得るためにGUIクラスをGameplayクラスに渡すことになります。この時点で、GUIクラスはあるべき姿以上のものになっています。TextureAnalyserクラスの役割を果たしているのです。解決策は単純です。該当部分をTextureAnalyserクラスとして切り離し、GUIクラスとGameplayクラスの両方から使えるようにするのです。

この問題の一般的な回避策は、追加しようとしている機能がクラスの名前に合致しているかを常に自問することです。違うのであれば、クラス名を変更するか、別のクラスに分けるか、他のクラスを利用するかのいずれかの方法をとります。

クラスに適した名前を思いつかない時は、大抵コードに不吉なにおいがします。クラスが行うことを名前で表せないのであれば、そのクラス内で実行する処理の秩序がなくなっているかもしれません。理にかなっていて、適切な名前で表すことができる部分に分割する必要があるかもしれません。

大きすぎるクラス

これは、上述の無秩序なクラスと非常によく似たものです。時間と共にクラスに追加されるものが増え、肥大化していきます。ただしこの場合は、その全てが1つのクラスの中にあること自体は理にかなっています。それでも、クラスが大きくなりすぎるのは良くありません。巨大なクラスは、扱うのが大変です。同じprivateメンバ変数を扱うコードが多数存在するため、バグが発生しやすくなり、見落としやすいディテールが多くなってしまうのです。

大きくなりすぎたクラスを分割するのはかなり退屈な作業です。そしてクラス内のコードが複雑に絡み合っている場合は難しくなります。しかも既に機能している上、修正したところで新たな機能が加わるわけでもありません。ですからここでも結論は同じで、肥大化してしまったクラスを分割するには、厳しい自己統制力が必要なのです。

Ronimoでは、目安としてクラスは500行未満、関数は50行未満にとどめるのが一般的になっています。時にはこれがあてはまらないこともありますが、大抵の場合は、クラスや関数がこのラインを超えたらリファクタリングをし、もっと小さくて扱いやすい大きさに分割する方法を探します。(皆さんはどの辺りで線引きをしているのかとても気になります。ぜひコメントで教えてください。)

コメント化されたコード

インターンの応募者が送ってくるほぼ全てのサンプルコードに、コメントアウトされたコードが含まれています。なぜコメントアウトされたかという情報はありません。壊れていて修正を要するコードなのか? 別のコードに置き換えられた古いコードなのか? なぜそこにあるのか? 何も分かりません。応募者に聞いてみると、大抵は本人たちもコメントアウトされたコードが混乱を招くことはよく分かっているのですが、それでもなぜかほぼ毎回入っています。

類似したロジックとコードの重複

私がよく見かける問題がもう1つあります。類似したロジックが複数の箇所にあるというものです。

例えばテクスチャの名前からは、”TreeBackground.dds” といったように、それが何を意図したものかという情報が得られます。あるテクスチャを木に使えるかどうかを知りたければ、ファイル名を見て”Tree”で始まっているかどうかを確認します。SDKが用いられている場合はfilename.beginsWith (”Tree”)を使うだけですぐに確認できてしまうでしょう。このコードはとても短いので、複数の箇所で使う必要があれば、ペーストするだけで使えます。もちろん、これはコードの重複であり、皆さんもご存知のとおりコードの重複は避けるべきものです。しかし重複したコードがとても短いと、コピーしてしまおうという誘惑に駆られます。ここで私たちが直面する問題は明らかですね。後になって、テクスチャが木に使えるかどうかの確認方法を変更するかもしれません。すると、これを使用している全ての箇所で細かい変更が発生し、それぞれの箇所を個別に修正しなければならなくなるのです。

当社の慣用ルールでは、コードが非常に特化している場合はそれをコピーするのではなく、関数に組み込むことにしています。たとえコードがものすごく短くて、直接実行するよりも関数を呼び出す方が多くのコードを要するとしても、そうします。

このブログの中で議論したことは全て分かりきったことです。ほとんどは大学1年生で習うようなことですね。何が課題かというと、これを単に知っているという状態から一歩踏み出して、常に最後までこれに従ってやり通そうと、実際に時間を費やすことです。そして普段からこれを心に留めておくことです。だからこそ、プログラミングのインターン生たちがRonimoで学ぶべき最も重要なことは、知識ではなく自己統制力なのです。