gallu’s blog

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

メソッドはどれくらい細かく切り分けるか?

以前から書こう書こうとは思っていたのですが、こんなツイートを拝見したので「あぁいいタイミングかなぁ」と思って。


https://twitter.com/kis/status/590760562654650370

「コードの意図はコメントではなくメソッドであらわすべき、細かすぎるコメントは実装と乖離して負債になる」っていう指摘があったけど、これそのまま「細かすぎるメソッドは実装意図と乖離して負債になる」とそのまま言い換えれるし、むしろメソッドのほうがメンテが面倒で負債としてでかい気がする。


追いかけて拝見をしていると
http://d.hatena.ne.jp/nowokay/20150420#1429507297
http://d.hatena.ne.jp/nowokay/20150421#1429579125
コメントの話、ぽい感じではあるのですが。
まぁその辺はあんまり気にせず、書きたいことを書いていきましょう(笑


以前にも何度か話をしたり書いたりしていて、一定のエリアの方々から眉をひそめられたものではございますが。
端的にはおいちゃんは「あんまりメソッド切るの好きぢゃない」です。
OOPな言語ばかりやってるんでメソッドいいますが、C言語でしたら「関数」言ってもらって何の差異もございませぬ。C++だと「クラス関数」だしねぇ(笑


先に結論を書いておくと、とりあえずおいちゃんが「忌むべき」と思っているのが「一箇所から一回しか呼ばれない、且つ、呼び元と呼び先が同一クラス内に共存している」メソッド。


もう一つ前提にあるのが、おいちゃんの流儀の一つ「共通化は二回目( http://d.hatena.ne.jp/gallu/20150422/p1 )」。
ちょうど先日書いたばかりでございます(笑


まず「あちこちから何度も呼ばれる処理」はそもそも「メソッドとして切り出すべきもの」であり、そうでなければ滅びの火山行きなので、是も非もありません。
ここに議論の余地は認めません*1
「共通化をすると難しいから」とかほざく愚か者に永遠の呪いあれ!!*2


次に「一箇所から1回しか呼ばれない」けど、書く場所的に「呼び出し元と呼び出し先が明らかに別クラスである」場合。
まず議論考察のポイントとして「本当に、それは、書く場所を変えなきゃいけないのか?」という疑問が出てきます。そしてその疑問は割と速やかに「クラス分け」に波及して、すなわち「これらのクラスは、本当に、複数のクラスである必要があるのか?」という疑問にたどり着きます。
おいちゃんがそこを判断する質問は、大体、こんな感じ。


「そのクラス達を1つにまとめたときに、実際に想起される弊害を述べよ。それは"美しい"という抽象的な発言ではなく、もっと具体例を伴って"明らかに実害が出る"と論理的に解説が可能か?」


実害が「ある」んなら別に躊躇なくクラスを分ければよいのですが。
そこで「口ごもる」とか「過去の慣習が」とか「ルールで」とか「今までそうだったから」とかいう程度なら、速やかに「1つのクラス」にしていただきます。


その辺を踏まえた上で「書く場所的に「呼び出し元と呼び出し先が明らかに別クラスである」場合」は、これは、別メソッドにしておいたほうがよいですっていうか書く場所が違うんだからメソッドに切り出さざるを得ない。


で、この辺を経過して「一箇所から一回しか呼ばれない、且つ、呼び元と呼び先が同一クラス内に共存している」メソッドであれば、おいちゃんは「切るな」言います。


ちなみにこの結果として「100行を優に超えるメソッド」が存在しえますが、それについては「100行を超える"から"駄目である」とは言いません。
実際問題、それなりに「長い処理」ってのは存在するので。
んで、その「長い処理」を「見やすくするために」メソッドで細切れにされると、ぶっちゃけそっちのほうが「可読性が落ちる」。
上から下に文章読むのと「あちこち参照で目を移動させる」のと、どっちが読みやすい? って話でございます。


一つの可能性として「丁寧にユニットテストを書いていて」っていうケースがあって、テスタビリティ前提である可能性があって。
その場合は「可読性が落ちる」より重要度の高い理由なので、おいちゃん的に許可。
ただ現実問題として「ユニットテストの粒度の故に、細かくメソッドを切った」とか、口上は聞いたことあるけど実例見てないんで、最低限この話を納得するためには「テストコード見せろ」って話にはなるかな*3


で。
「一箇所から一回しか呼ばれない、且つ、呼び元と呼び先が同一クラス内に共存している」メソッドを散らかされると、どうしてもソースコードを追いかける時に面倒なのですが。
より面倒なのが「一箇所から一回しか呼ばれない、且つ、呼び元と呼び先が同一クラス内に共存している前提で、privateなプロパティとかを直接触ったりしている」ケース。更にそこのプロパティに「予めこの値が入っていることを期待している」とか、大変に密結合な設計だったりする事も少なからず。
後で必要になって「親クラスに持ち上げる」とかなんか移動が発生した場合に、割と簡単にエラーったりして、移動一つできないので面倒ったらありゃしない。
近似値で「メソッドになってるから別の場所でもこれを使う事になったから使おうとしたら使えない」とかも、大変にいとわろし。


更に個人的にお好まないのが「分けなくていいクラスを無駄に分けて、かつメソッドを細かく切って散らかしてる」ケース。
読みにくさが半端ない。
そこでさらに「他クラスの状態含めての密結合」とかやられた日にゃ、殺意しか湧きませんなぶっちゃけ。


そんなのを見ていると

「細かすぎるメソッドは実装意図と乖離して負債になる」とそのまま言い換えれるし、むしろメソッドのほうがメンテが面倒で負債としてでかい気がする。

ってのに、思うところが山盛りで出てくる訳でして。


で、そこから発端して「じゃぁメソッドってどこで切り分ける?」って考えると…
・基本は「共通化は二度目」
・クラス的に「どう考えたってこれは別クラスにすんだろ」ってクラス切りの中での「別クラスの処理」であれば、疎結合に配慮しつつ切り出す
・テスタビリティ的に「どうしてもここで一回処理を切って、テストしたい」ってご要望があるなら、やむなく…
って感じになる訳なんですね。


なんかあちこちの半可通なところで「1メソッドはn行(nは大体、10〜50くらい)以内」とかいう適当な指針をぶちたてて、それを信じちゃった人達がとにかく「細かくメソッドを分ける」風潮、ってのが一定数見受けられるんですが。
後で解析するときにマジ可読性悪いんで、是非、そーゆー風潮には滅んでいただきたいかなぁ、とか思っている訳です正直。


なんとなく、ブックマーク上のコメントが「荒れる可能性」を想起しつつ、もし「的を射た批判」があったら面白いなぁ、とか微妙に期待もしつつ、書いてみました。
できたら「コメントに」もらえるほうが嬉しくはあるんで、よろしかったら。
「信仰に支えられたコメント」でない限り、会話は歓迎いたしますので。

*1:極めて特殊な例外はあるのですが、とはいえ「マクロ書けよ@C言語」とかになるしなぁ…コンパイルオプションで最適化すりゃいいぢゃん、とかって見地もあるし…

*2:伝聞ではあるにしても実話だったりするから、この業界も闇が深い…

*3:下にある「密結合」あたりのお話は、テストコードによって明らかになるので、ここでは一端オミット