プログラミングテクニックのまとめ

とりあえず思いついたもののまとめ。


まずは、ベーシックなものから。

  • 変数のスコープをなるべく狭くしろ

他はグローバル変数を使うなとか、モジュール化と界面を意識せよなど。とにかくスコープは重要かつ意外と奥が深い。スコープに関係する機能は、モジュール(パッケージ)、クロージャ、ローカル関数、ローカルクラス、変数の種類、アクセス制御など。

  • 同じロジックのコードを2度以上書くな

他はDRY原則、コピペをするななど。自分の場合、2度書く方がシンプルになる場合、2度書くこともある。特に、ifやswitchなどのロジックの中で同じコードが2度現れる場合、ちょっとしたコードでわざわざ別のところで関数やブロックにまとめて、それを参照するのは面倒。但し3度以上現れる場合は関数などにまとめるケースが多いかも。

  • 汎用コード内で条件分岐コードを減らせ

他はifをポリモーフィズムによりなくせなど。条件分岐は汎用性を損なう原因となる。もしくは、1つのロジックの中に2つのロジックが混ざってしまっていて、きちんと分離できていない証拠。テクニックとしては、汎用的なコードをある関数にまとめた場合、その関数の外に条件分岐コードを追い出す。インタフェース、Duck Typing、ハッシュ(辞書)などを利用してifやswitchをなくせる。


次に、自分のセンスや主観的なもの。

  • Simple is the best

他はKISS原則(Keep It Simple, Stupidなどの略)、パレートの法則(80:20の法則)、富豪的プログラミングなど。私のプログラミングにおける座右の銘である。ソースコードは書く時間より読む時間の方が何倍も多いと言われているが、保守が重要になるプロジェクトでは書きやすいよりも読みやすいコードの方が重要になると思われる。もちろんそういう言語を選択することも重要。

  • 開放/閉鎖原則(OCP; open/closed principle)

よくオブジェクト指向の原則みたいに書かれているが、オブジェクト指向に限らずシステムは基本的にそうあるべきだと思う。但し拡張性を持たせることが必ずしも真ではないとも思う。機能を追加するたびに既存のコードを修正しないといけないのは、依存関係が複雑になっている証拠。もしそういう傾向があるなら機能追加のたびに複雑性は指数関数的に上がっているはず。基本的に、機能追加はなるべく線形的な複雑さで行えるべき。すなわちOCPやモジュール化。メタプログラミングとの関係も要チェック(⇒自分)

  • ケースバイケース

他は狼人間を撃つ銀の弾はないなど。ほとんどのケースにおいては、ケースバイケースであると思う。100%守るべき原則というのは非常に少ない。例えば、時間がないプロジェクトや使い捨てコードで汎用性にこだわってもしょうがない。状況によって何を優先させるべきかでコードは変化する。

他はいろいろなプログラミング言語を勉強しろなど。この意見はいろいろな言語を勉強しろと対極な考えであると思われるかもしれないが、私から見れば同じことである。つまり、いろいろな言語を勉強すべきだが、中途半端にならずに1つか2つの言語を極めろということである。言語は奥が深い。極めれば極める程すばらしいコードが書けるようになる。言語の勉強に終わりはない。

  • コードの階層化を意識せよ

ソースコードの階層はフラットではない。関数呼び出しを重ねていくとだんだん上の階層になってくるが、上と下の関数は明らかに意味が違う。私は以下のように分類している。一番下の階層がアプリに依存しない汎用的な処理(ライブラリ)である。次の階層が分野(ドメイン)に特化した汎用的な処理(ライブラリ)である。次の階層がアプリ自体の処理(業務ロジック)である。フレームワークなども考えるともっと階層化されるかもしれない。しかしこの程度でも意識するのとしないのでは、ものすごく違いが出ると思われる。


他は気がついたら追記していく予定。


追記(2008/10/21):

  • MVCを分離せよ

他はデータをハードコーディングするななど。データ、処理、表示を1つのコードの単位(モジュール)の中に混在させるな。分離せよ。

  • テストコードをつけよ

他は単体テストを作成せよなど。テストしやすいようにプログラムを構成すべき。テストしやすいコードはデバッグしやすいし、可読性も上がると思う。例えば、Pythonでベース処理関数にdoctestを付けると関数の挙動を簡単に理解できる。

  • DSLを意識せよ

DSLはうまく使わないとかえって分かりづらくなるというイメージが私の中であるが、うまく使えばコードの可読性が上がったり、例えば正規表現など既存のものを使用した場合にはパフォーマンスも上がるかもしれない。

  • 遅延処理をうまく使え

遅延処理を使用すると動的性を増したり、結合性を弱くできると思う。しかし状態を変えるタイミングがずれるという意味で若干、黒魔術的になる可能性もあるので注意が必要。例えば、Pythonで__getattr__特殊メソッドを使用すると属性に初めてアクセスしたタイミングで属性を構築できる。例えば、追加コードの中であらかじめ想定していなかった属性をクライアントコード側で使用できるので、動的性が増したり、結合性を弱くできると言える。


追記(2008/12/11):

  • 物理的な意味と論理的な意味を区別せよ

これはコードの階層化を意識せよと関連するかもしれない。例えばハードウェアなどのデバイスを考えてみると複数の異なったマシンを統一的に扱うためにOSがあり、複数の異なったOSを統一的に扱うためにVirtual Machineがある。よりアプリケーションに近い処理ではロジックを物理的な意味でなく論理的な意味で抽象化して扱えるようにした方が良い。他にはデータに対してどういう意味を与えるかで処理が変わってくるが、より論理的な意味で捉えるとその部分のロバスト性や汎用性が上がる。ロバスト性が上がる理由は、より低レベルな階層に物理的な意味を論理的な意味に変換する処理を押し込めることができるからである。


追記(2011/12/1):

「いずれ必要にならない。」将来を予測して必要だからと機能を追加しても、結局は使わず、無駄に終わること。現在必要な機能のみ実装して、シンプルに保つのがベストという教訓。Wikipediaから引用すると、機能は実際に必要となるまでは追加しないのがよいとする、エクストリーム・プログラミングにおける原則である。