by chazmatazz
「構造のきれいなプログラムを書けるようになるためにはどうすればいいのか?」という質問を受けたので、「はて?どうしているだろうか?」と考えてみました。あ、形式知にきちんとなっているようなテクニックみたいなもんじゃなくて、モノローグなので、あまり凝ったものは期待しないように。あ、Pythonに限定してますが、他の言語でも似たようなものはあると思いますので、脳内変換をお願いします。
事前の設計はしません
「こういう処理が必要」「こういう計算しなきゃね」みたいなロジックや「要件はこうかな?」ということは事前に考えたりするけど、クラス構造とかは基本的に考えないで手をつけます。そして、ある程度規模が大きくなって「あ、ちょっとこの関数大きすぎて理解しにくいなぁ」と思ったら分割してみる、という感じです。これの繰り返しです。事前の設計では分からないことが多すぎて、最適解にはたどり着けません。リファクタリングというやつですね。アジャイルな見積りと計画づくり
にも書かれていますが、設計を徐々にしていくというのに否定的で事前にがんばろうとしちゃう人/途中でクラス構造などを変更しない人は、「実装中の学び」をゴミ箱に捨てているのと同じなので、もったいないことをしていると思います。
最初は単なる関数で実装し、関数同士のグループが見えてきたり、関数の呼び出し順が固定化されてきたら、まとめてクラスメソッドにしてクラスという枠の中に整理したり、複数インスタンスを作る必要があれば、インスタンスメソッドにしたり・・・。クラス同士が似てれば共通の親クラスを用意してみたり、メソッド名を修正したり・・・
普通の人よりはクラスメソッドとか、グローバルな関数やグローバル変数(といってもPythonの場合はモジュールという名前空間で分割されるので、完全なグローバルにはならない)は比較的多く使っているかもしれません。一度しか呼ばれない処理で独立したもの、あるいは状態を持たないものなどは最後まで関数のまま残ったりします。Javaみたいにクラスに入れればオブジェクト指向になって美しい設計になる、なんてことはこれっぽっちも思ってません。責任区分を間違ったクラスは理解を妨げます。そんなクラスを作るぐらいなら、フラットなモジュールレベルの関数の方が害が小さいと考えています。
重要なのは内面を見ること
「ちょっと喉の調子が悪いから風邪引きそうだな」とか「今日のアイアンのスイングは調子いいなぁ」と感じる能力ってありますよね?例えば、イスにリラックスして座ってみて、そのときに体の重みがどのようにイスにかかっているか、自分の呼吸がどうなっているか、というのをじっくり観察する、というのがこの感覚を思い起こすトレーニングになるかな、と思います。スポーツをやっていたとしても、音楽をやっていたとしても、上達をするには、パフォーマンスをするのに精一杯、というのではだめで、「次はこの部分を直してみよう」みたいな視点は常に必要です。ちょっとメタな視点。
ある程度普通に生活をしていて、普通に趣味をいくつか持っていればある程度そういうことはできると思いますが、引きこもりで一人で生活していた子にマンツーマンで授業を教えても、なかなかこの感覚は理解してもらえなかった、とSRRビジネス速読の寺田さんが言われてました。社会性というのは自分をのばさなければ、という動機になりますからね。人と接することは自分を成長させる、といういい例だと思います。
そいういう意味で、合唱をやっていた、というのは僕のプログラミングのスキルにそれなりのプラスになっているのかなぁと思います。喉のコンディションに配慮をしつつ、声が響くポイントを探して発声する・・・とかね。
不吉な匂いって?
最初の項目から、次の項目にかなりギャップを感じられたかもしれませんが、なんでそういうことを書いたかというと、リファクタリングの本にある「不吉な匂い」というのを論理的に説明する手段が今のところ見つけられていないから。「3回同じのが出てきたらリファクタリング」みたいな書かれ方もしていたけど、実際にコーディングをしていると、そう簡単に割り切れるものでもないし。
コーディングをしていて、「あ、今書いている関数は大きくなってきたので、ぱっと頭の中に入りにくいなぁ」とか、「似たようなメソッド名で、どっちを使えばいいのか後で見たらわかりにくいなぁ」とか、コーディングをしている時の「違和感」というのが、コードをリファクタリングするきっかけになることが多いかな、と思います。そういう意味では、自分の中の違和感の声に耳を傾けるところから「きれいなコード」が始まるんじゃないかと思います。
人間の脳のワーキングメモリは7±2と言われます。一つの関数の中で変数を10個も20個も作って、長大な関数を作ってしまうと、中で何が起きているのか分からなくなります。逆に関数を細かく分けすぎて、一つの結果が出るのに10個も20個も分割されたメソッドを渡り歩かないといけない、となっても理解が遅くなります。リファクタリングの本の後半はカタログになっていますが、「大きくする」のと「小さくする」という対極の手法が両方とも書かれています。「こっちの方が違和感が少ないなぁ」という方法を選択しないことにはリファクタリングはできません。
もしかしたら、リファクタリングの本にもこういうことは書かれているかも。妹に貸してしまって、手元に本がないので適当に書いてます。
最近気をつけている習慣
そうは言っても、違和感を減らして、というか違和感を感じやすくするためにできる工夫というのはいくつかあります。以下は僕が最近取り組んでいる方法です。まぁ、最近始めたわけでもないですが。
epydoc形式でコメントを付ける
epydocというのは、JavaDocみたいなものです。Pythonには元々pydocがありますが、これはそれの機能強化版です。Pythonは型がないので、変数もオブジェクト自身が入るのか、そのオブジェクトへのインデックスが入るのか、検索キーが入るのか、たまに迷ったりします。たまに振り返って型を整理して書くことでムダが見えたりします。また、ドキュメントには「何のために?」という質問の回答を書きます。常に理由を問うことで、作った関数が迷子になる(どこから何の目的でどんなタイミングで呼ばれるか分からなくなる)のが防げます。また、PyScripterでプログラミングしていると、ポップアップで情報が出てくるので便利です。
また、ドキュメントは「なぜ?」ではなく「なんのために?」という視点で書きます。ワンランク上の問題解決の技術という本で、この2つの疑問系の違いについて書かれていますが、「なぜ?」というのは攻撃性の高い言葉です。なぜなぜ分析はやったことがありますか?やられたことがありますか?なぜなぜって「おまえが悪いんだ。言い訳があるなら聞いてやる」みたいなトゲを感じるんですよね。その点「何のために?」は「私はあなたの味方です。きっと何かを良くしようと思って行動したんですよね?よろしければ理由を教えて下さい」という態度表明になります。Django hack-a-thonでも言いましたが、「なぜ」という言葉が無くなれば日本は良くなると思います。
名前にはこだわる
最良のメソッド名は最良のコメントということで。関数名、変数名は意味を伝える重要なラベルです。引数が増えてしまった場合にはキーワード引数を使うのも見やすさがアップします。
# こういう呼び方もできるけど calc_position(100, 200, 10) # たまにはこういうこともします calc_position(x=100, y=200, depth=10)
あんまり長すぎても読みにくくなってしまうけど、基本は長く。長すぎるメソッド名というのは、そのクラスの責任区分から離れた処理である可能性が高いので、別のクラスを作成するきっかけにもなります。名前をしっかり付けることで、見えてくるものはたくさんあります。名前の修正だけで1日かけたりすることもあります。
スコープを意識する
僕は、プライベートなメソッド(外から呼ばれない)の名前にはアンダースコア(_)を前に付けます。C++で書くときは後ろですね。前のアンダースコアは言語規約で処理系用の予約語になってますからね。これのメリットとしては、コードをざっと見るときに「切り捨て」ができることです。「あのクラスってどういう仕事をしているんだっけ?」と思ってコードを見直したときに、アンダースコアが付いていない処理だけを見れば、他のオブジェクトに対してどういうサービスを提供しているかが分かります。7±2しかない脳みそのワーキングメモリを節約するためにも、「名前でフィルタリングできる」ようにする、というのは大切だと思います。
メソッドや関数の中も、if文のネスト(3段になると、2^3=8でワーキングメモリをオーバーする)が深くならないようにするとか、独立した処理の島を別の関数に分けたり、といったことをして、一つのスコープ内を見たときに、がんばって解読しなくても把握できるようにします。
色々書いたけど
やっぱり、一番重要なのは、「このレベルなら自分はすぐに理解できる」「これより複雑だと理解が大変」という複雑さの尺度を自分の中に作ることだと思います。経験の差が効いてくるか、というと、Pythonにはあんまり関係ない気がします。Rubyはちょっと効いてくるとは思いますが。過去にブロックが3つぐらい連結されているのを見て「あ、ムリ」と思って一回あきらめた口ですので。イディオムをどれだけ理解するか、というのによって理解できる範囲は広がったりするんですが、Pythonだとイディオムってあんまりないんですよね。もちろんゼロではないですが。そういうことなので、経験を積まなきゃ、という強迫観念を持つ前に、「今の自分のレベルではすぐに理解できる複雑度はこんなもん」というのを身につける方が、気持ちいいプログラミングへの早道かな、と思います。尺度が見えている方が、自分のスキルアップも客観的に見えて、勉強も楽しくなると思いますしね。
こんどは「何のために」というのが「なぜ」の意味を持ってくると思いますw
なぜ?