分かりやすいコードの書き方
第3話:適切な関数の分割
(最終更新日:2025.4.6)
(絵が小さい場合はスマホを横に)
適切に分割しよう!
コードが読みにくくなる原因の一つが「関数が長すぎて、何をしているのか分からない」ことだ。
始めは短くて分かりやすいロジックだったとしても、
機能が追加され、ロジックを加えていくうちに分かりにくくなることはよくある。
今回は、そんな関数をどのように分割すれば、読みやすく、保守しやすく、再利用性の高いコードになるのかを解説する。
1.1つの関数は1つの責務
まず大前提として、関数は「1つの目的」だけを果たすように書くべきだ。 これを単一責任の原則(Single Responsibility Principle)と言う。 時と場合にもよるが、基本的には、1つの関数に処理を詰め込みすぎないことが重要だ。 コメントで省略している(実際にはif文、for文などの処理で非常に長くなる)が、下記のような関数は良くない。 1つの関数に複数の責務(チェック・計算・通知・更新)が含まれていて、テストしづらく、再利用も困難になる。
分割されていない関数
次に良い例を示す。関数の中に小さな関数で分割されているのが分かる。 1つの関数に1つの目的だけを果たすようにしているので、テストがしやすく、再利用も容易だ。 また、適切に関数が命名されていれば、どんな処理をしているのかも明らかになる。 先ほどのように、コメントで記さなくても関数が処理を示すので、非常に読みやすい。
分割された関数
2.適切な関数の長さと関数分割の基準
一概に「何行以内」と断言はできないが、10〜20行以内が適切な関数の長さの目安と言える。 ただ、長さよりも読み手への分かりやすさを重視した方が良い。あくまで分割の目安である。 画面にスクロールせずに収まり、各関数間の抽象度が揃っていることが大事だ。 テストや再利用をしやすくするためにも、小さくて明確な関数が良い。
次に関数分割を行うか否かの基準だが、次の三点を基準にすると良い。
- 処理が複雑になった(複数の処理を1つの関数に書いていると読みにくくなる)
- 同じ処理が複数箇所にある(重複コードは分割して共通化)
- 関数名がうまく付けられない(複数の責務が混在している)
3.命名、分割しすぎのリスクとその回避方法
関数名は「何をするか」を明確にするために動詞から始めるのが基本だ。 下記に悪い例と良い例を示す。悪い例(名詞、もしくは動詞だけ)だと何をどうするか分からない。 一方、良い例では「動詞+目的語」で書かれているので、何をどうするか(E-mailを送る)が明らかである。
悪い例 | 良い例 |
---|---|
data() | loadData() |
email() | sendEmail() |
check() | checkInventory() |
update() | updateUserProfile() |
今まで、関数は分割するようにと言ってきたが、関数を小さく分割しすぎると、 かえって処理の流れが断片化してわかりにくくなることがあるので、注意が必要だ。 適切な粒度で分割することが求められる。 下記に悪い例と良い例を示した。名前にした時、処理の流れが想像できるのが理想だ。 「動詞+目的語」でスッキリ命名できない場合は、処理が小さすぎるか大きすぎると思っておこう。
分割しすぎ(悪い例)と適切な分割(良い例)
4.リファクタリングの実例
上記の集大成として、リファクタリングの実例を示す。 まずは、リファクタリング前の、分割されていない関数を示す。 「Welcomeメッセージの作成」と「E-mailの送信」、2つの処理が混在していて読みにくい。
リファクタリング前
次にリファクタリング後の実例を示す。 「ユーザー名を引用したWelcomeメッセージの作成」と「E-mailの送信」を分けたことで意味が分かりやすくなっている。 加えて、2つを合わせた場合よりも、各々の関数でテスト、再利用しやすくなったことが分かる。 例えばE-mailの送信は、今回のようなWelcomeメッセージが無い場合でも使える。また、変更もしやすい。
リファクタリング後
5.まとめ
今回は関数の分割方法について解説した。 関数は1つの目的だけ持たせて、長さはできれば20行以内が好ましい。 ただし、分割しすぎて使いづらい時は、長さと命名を再度考える。 重複コードや複雑な処理は分割のサインになる。そして、関数名は「動詞+目的語」で意味が分かるようにする。 このような創意工夫が、あなたのプログラムを読みやすいコードにしてくれるはずだ。
▼参考図書、サイト
「リーダブルコード」 Dustin Boswell、Trevor Foucher 著、角 征典 訳 オライリー