
はじめに
この記事は, Kent Beck著 「Tidy First?」を読んで, 私が何を学んだのか, 何を思ったのかを記録するための記事です. 読書体験を共有することが目的であり, 当然ですが本書のすべての内容を説明することを目的としたものではありません. 訳本も出ているようなので, ぜひご自分で手に取ってみてください.
コードの片付けとは
片付けとは, リファクタリングの小さな小さなサブセットです. "片付け"の例として紹介されていたものから, 個人的に印象深かったものをいくつかピックアップしてご紹介します.
パススルーインタフェース
今あるライブラリやコードベースが提供するインタフェース (関数の呼び方, 名前など) が使いやすくなかったら, 自分が欲しい形のインタフェースで包んでしまいましょう.
作ったインタフェースをはじめから実装しなおす必要はありません. 今はとりあえず, 古いインタフェースを使いましょう. インライン化が必要ならばあとでやればOK.
interface ComplexInterface {
function someComplexFunc(complexParams);
}
interface PassthroughInterface {
function someGreatFunc(simpleParams) {
// build args
complex = ...
// call old interface
old.someComplexFunc(complex)
}
}
このような, 薄い実装をもったインタフェースを, パススルーインタフェースといいます. パススルーインタフェースは, 本質的な意味のないコードではありますが, コードを改善するためのヒントが詰まっています.
ヘルパに抽出/インライン展開
コードの中で, 明確な目的があって, ほかの部分とのやり取りが限られている部分があれば, これをヘルパに分離することができます. ヘルパとして分離することによって, ガード節が使えるようになったりもしますね. ヘルパが多すぎると思っても, 今は気にしなくてOK. 後で片を付けましょう.
function someFunc(args) {
if (args is empty) return;
if (args is invalid) return;
if (args does not exists) return;
if (user is not authorized) {
authorize
}
....
}
---
function someFunc(args) {
if (not is_valid(args)) return;
authorize(args);
...
}
function is_valid(args) {
if (args is empty) return false;
if (args is invalid) return false;
if (args does not exists) return false;
return true;
}
function authorize(args) {
if (user is not authorized) {
authorize
}
}
一方で, コードが細切れ過ぎて理解しにくいと思ったら, インライン化するのもありです. 理解できるようになるまでインライン化を進めて, まとまったコードにしましょう. 特に, 次のことに苦しめられているなら, インライン展開(piling)は有効です.
- 長くて, 繰り返し使われる引数のリスト
- 繰り返される条件分岐
- 下手な命名のヘルパ関数
- いろいろなところで共有されている可変データ
function someFunction(args) {
mutable_args_holder = Holder(args)
artifact1 = helperA(mutable_args_holder)
artifact2 = helperB(mutable_args_holder, artifact1)
artifact3 = helperC(mutable_args_holder, artifact2)
}
---
function someFunction(args) {
// helperA,B,Cの中身を展開
// 可変参照を渡す代わりに直接変数を触る
}
「ヘルパに抽出しろと言ったり, インライン展開しろと言ったり, 真逆じゃないか! 一体どっちなんだ! 」 と思った方. わかります. でも, この矛盾から"片付け"に対する向き合い方が見えてきます.
片付けのあるべき姿
片付けを日々のコーディングに取り入れる前に, よくよく踏まえておいてほしいことが二つあります.
ひとつは, 片付けは, 目的をもって行うこと. 普段, 何かを片付けるとき, 片付け自体が目的でしょうか. 片付けて, 何かの置き場を作る. 片付けて, 何かの作業スペースを確保する. きっと片付けのその先に目的があるはずです. コーディングでも同じ. 片付けの先には目的があるべきです.
もうひとつは, 片付けでは, 範囲を絞ること. ソフトウェア開発においてレビューコストは無視できない要素です. これを踏まえたとき, 片付けというアクティビティを, 机上の空論ではなく, 実行可能で持続可能なプラクティスとするために変更の範囲は絞らなければなりません.
片付けの目的
片付けの目的は大きく分けて2つ. コードを理解することと, 変更を簡単にすることです. 正解を目指すことは, 片付けの目的ではありません. 目的によって, 片付けをいつやるべきか, そして, いつ止めるべきかが変わります.
もし, あなたが, プロジェクトに参画したばかりで, コードベースを理解する段階にいるとしたら? 「まず片付けから」というのはいい考えです. コードを読んだ体験を通じて, コードをより読みやすくすることができますし, ただ漠然と眺めるより理解が進むでしょう. そして, ある程度理解が進んだら, それは片付けをやめるときです. 「まだ片つけられていないところがある」? であれば, コメントをつけておき, 後で時間のある時に片付けるのはいかがでしょうか. 片付け自体が目的にならないようによくよく注意してください.
もし, あなたが, システムのふるまいを変更しようとしていて, 既存のコードベースが散らかっていて, 変更がしずらいと思うなら, 「まず片付けから」始めるのは, やはりいい考えです. しかし, 片付けの対象はよく考えなければなりません. その片付けをして, ふるまいの変更は簡単になるでしょうか? 関係のないところに手を伸ばそうとしてしまったら, それは片付けをやめるときです.
コーディングをしていて, 特に変更の予定があるわけじゃないけど, 散らかっていて気になる部分を見つけた, ということもあるでしょう. 暇なら, そこを片付けてもいい. でも, 暇なんてことはないでしょう. そういうときは, コメントだけつけて, 後で片付けるリストに入れておくのが吉です. 本当に暇で仕方がないとき, もしくは, 実際にそこにまつわる変更が計画されたとき, 片付けに取り掛かるのがいいでしょう.
片付けは, システムのふるまいを変更しません. (というより, システムのふるまいを変更してはいけません.) つまり, ビジネス的には即座の価値を生まない行為です. (\* 即座の価値ではなく, "可能性"という遅効性の価値を生む行為ではありますが. ) 人件費が支払われる職業プログラマは, 自身の行動が価値を生むか (金を稼げるか) 考えて, 行動を取捨選択しなければなりません. コードの片付けは楽しいものですが, 価値を生まない言い訳にはならないのです. 一日5分や10分ならともかく, 2時間も3時間も無為な片付けに費やしているのでは, あまりにも人件費が無駄でしょう?
片付けの範囲
上の章では, 片付けの範囲を目的に合わせて絞ることが重要だと述べました. これに加えて, 片付けの範囲をレビューに合わせて絞ることも重要です.
片付けの目的が, 変更のコストを小さくすることだったことを考えると, レビューコストを無視することはできません. 理想は, 片付けに関するプルリクエストは, レビューをスキップできることです. (難しいかもしれませんが, 試す価値はあります.)
レビューコストを抑えるための工夫の一つは, 片付けのカタログを用意しておくことです. チームメンバーが思い思いに片付けをしていては, レビューが大変です. そこで, チームで合意した片付けのカタログを作っておき, 片付けと題したコミット (プルリクエスト) では, そのカタログから逸脱したことはしないとすることで, レビューコストを抑えることができます. コミット(もしくはプルリクエスト)の粒度をカタログの1項目に合わせると, よりレビューがしやすくなるでしょう. 原著「Tidy First?」では第一章にこのカタログのサンプルが用意されています. カタログが適切に運用できていれば, レビュースキップも夢ではないでしょう.
また, 片付けの範囲を目的に合わせて絞ることは, 時間を有効に使うという意義を超えて, レビューの負担を減らすことにも意義があります. 散逸的にいろいろなところを変更した大きなプルリクエストと, 一貫した目的をもった局所的な変更からなる小さなプルリクエスト, どちらがよりレビューしやすいかは明白ですね.
カタログ作りの理論
原著の第三章では, "なぜ片付けるのか"への答えとして, ソフトウェアデザインの理論が紹介されました. ここでは, その中から片付けカタログ作りに役立ちそうな理論を取り出して論じてみます.
可逆性
片付けは, 容易に差し戻せるべきです. ヘルパ抽出/インライン展開は差し戻しの一つの例です. 物事には必ず, 良い面と悪い面があります. そして, 良い面が我々を助けてくれるのか, 悪い面が牙をむくのか, それはやってみないとわからない節がある. しかしながら, 可逆な変更であれば, 悪い面を避け, 良い面だけを享受するのは容易です.
片付けのカタログを作るときは, その変更が可逆か注意してみてください.
結合
結合とは変更に基づいた関連です. 例えば, "関数名の変更"は"定義"と"呼び出し"の両方の変更を必要とします. よって, 定義と呼び出しが結合しているといえます. ただし, 結合とはこのような, わかりやすい形態のものばかりではありません. 過去のコミット履歴を見てみると, ファイルAとファイルBが一緒に変更されていることが多いなぁ, と気づく. このとき, ファイルAとファイルBは結合しています. どんな変更に基づいているのかは... コミットメッセージが丁寧に書かれていることに期待しましょう.
結合はソフトウェアを構築するにあたって不可欠ですが, 連鎖的な変更を誘起する厄介な存在です. 結合は減らせれば減らせるほど良いですが, 結合を減らすにはコストがかかります. ソフトウェアデザインの目的は, この結合のトレードオフを, いかにうまく扱うかというところにある, とすらいえます.
片付けのカタログを作るときには, その片付けが結合を減らすかを考えてみてください.
凝集
凝集とは, 結合を減らさずに, うまく扱う一つの方法です. 結合した要素の親要素を同一なものにし, 結合していない要素は外に追い出す, というのが基本的なアイディアです. これによって, 変更漏れによる不具合を低減することができます.
片付けのカタログを作るときは, その片付けが凝集を強めるか考えてみてください.
まとめ
「まずは片付けから?」という質問への答えは, 「必要なら.」 になるでしょう. 日々のコーディングに使えるプラクティスを提供してくれたのはもちろんですが, 盲目的に何かに取り組むのではなく, 目的をもって取り組むことが重要だということを思いださせてもくれた一冊でした.


