はじめに
この記事はUdon Advent Calendar 2025 - Adventarの23日目の記事です。
技術系バイトで実務に関わらせていただくようになってから早半年以上。様々なプログラムを組んできたわけですが、メソッドの作り方に関しては様々なレビューをいただいてきました。
今回の記事ではその学んだ内容を書いていこうと思います。
また、参考にさせていただいた記事のたとえ話が非常に分かりやすかったので、自分も「勉強」をテーマとした疑似コードを用いて説明を行ってみます。
とりあえず内容を詰め込んでみる
まずは適当に内容を詰め込んだプログラムを作ってみます。
今回のテーマは「勉強」なので、たとえば応用情報技術者試験の対策をやってみた、という感じにしてみます。
まずは勉強の流れをやることリストの形式で書いてみます。
机に向かう
勉強用具を用意する
前回の微妙だったポイントを確認する
前回間違えた問題を復習する
過去問を用意する
タイマーをスタートする
過去問に取り組む
タイマーを止める
過去問の答えを用意する
答え合わせをする
間違えた問題の解説を読む
解説のポイントをノートにまとめる
テキストで間違えた問題の関連事項を確認する
関連するポイントをノートにまとめる
勉強道具を片づける
机から離れる
このままだとわかりづらいですね。ここからこのコードを分けていくことになります。
やっていることのグループで分け、メソッドにまとめてみる
勉強と言っても流れがある訳なので、その流れに沿って上記のコードをグループ分けしてみましょう。
# 勉強の準備
机に向かう
勉強用具を用意する
# 前回の復習
前回の微妙だったポイントを確認する
前回間違えた問題を復習する
# 過去問への取り組み
過去問を用意する
タイマーをスタートする
過去問に取り組む
タイマーを止める
# 解答・解説のチェック
過去問の答えを用意する
答え合わせをする
間違えた問題にチェックを入れる
間違えた問題の解説を読む
解説のポイントをノートにまとめる
# テキストを用いた復習
テキストで間違えた問題の関連事項を確認する
関連するポイントをノートにまとめる
# 後片付け
勉強道具を片づける
机から離れる
グループ分けできたら、そのグループをまとめてメソッドにしてしまいます。もちろん、メソッドにしたので呼び出し部分を作って全体としての意味が変わらないようにもします。
疑似コードはPythonっぽいくしてみます。
def 勉強の準備
机に向かう
勉強用具を用意する
def 前回の復習
前回の微妙だったポイントを確認する
前回間違えた問題を復習する
def 過去問への取り組み
過去問を用意する
タイマーをスタートする
過去問に取り組む
タイマーを止める
def 解答・解説のチェック
過去問の答えを用意する
答え合わせをする
間違えた問題にチェックを入れる
間違えた問題の解説を読む
解説のポイントをノートにまとめる
def テキストを用いた復習
テキストで間違えた問題の関連事項を確認する
関連するポイントをノートにまとめる
def 後片付け
勉強道具を片づける
机から離れる
勉強の準備()
前回の復習()
過去問への取り組み()
解答・解説のチェック()
テキストを用いた復習()
後片付け()
各操作がまとまったことで、多少見やすくなりました。
1メソッドに1つの役割
まず意識することは、1つのメソッドに割り当てる役割を1つだけに絞る、ということです。役割というと少し難しいので、「操作」という面にフォーカスすると分かりやすいかもしれません。
例えば、以下のようなメソッドがあるとします。
def 勉強の準備
机に向かう
勉強用具を用意する
メソッド全体としては「勉強の準備をする」という1つの目的を達成しようとしていますが、その過程で複数の操作を行っています。
この「複数の操作」を基準としてメソッドを分けてみましょう。
def 机に向かう
椅子を引く
椅子に座る
def 勉強道具を用意する
ペンを用意する
ノートを用意する
テキストを用意する
このように2つに分けることができました(便宜上、それぞれに細かい内容をつけています)。各メソッドそれぞれが分ける前は1つにまとまっていた操作を担当する形です。
分けるポイントは「ほかでも使えるか」です。「机に向かう」は勉強以外の目的にも使うかも知れないし、「勉強道具を用意する」は教科によって内容が変わってくるかもしれません。
条件を細かく指定したいときなどには操作のまとまり全体で条件を指定するより、その部分部分それぞれに指定したほうが分かりやすいです。これがメソッドの小分けをするメリットの一つです。実際はこういう条件は引数でしていしたりすることとなります。
また、メソッドの小分けは不具合チェックにも役立ちます。
メソッドをテストしたときエラーが起きたとしましょう。この時、メソッド内に複数のどこでエラーが起きたのかわかりづらいですが(エラーログを読むことになります)、役割を分けておき、それぞれに対してテストをすればエラーの原因となっている部分を特定することができます。
このように問題も小さく切り分けることができるのも良い点です。
メソッドをつなぐメソッド
メソッドごとに役割を細かく分けましたが、分ける前のような細かい操作がまとまって一つの大きな操作を担っている場合はどうしたらよいでしょうか。
このとき、以下のような「メソッドをつなぐメソッド」が役に立ちます。
def 勉強の準備
机に向かう()
勉強用具を用意する()
先ほどと変わらない気がしますが、()が付いたのでメソッド内で
エントリポイントとなるmain関数とかrun関数などが有名ですね。基本的にこれらの中はあまり複雑にしない方がよいとされています。
これらは「メソッドとメソッドをつなぐ」ことが役割なので、1メソッド1役割の原則からも外れていません。
これらを定義するポイントは、「分ける前の動作を割り当てる」ということです。複数の操作がまとまっているメソッドがあったら、操作ごとに小分けし、さらにそれらをつなぐメソッドを作る、という感じに分割すると良さそうです。
def メイン
勉強の準備()
前回の復習()
過去問への取り組み()
解答・解説のチェック()
テキストを用いた復習()
後片付け()
細かく役割分担したメソッドをつなぐこのメソッドはいわゆる結合テストにも応用できるので、意識しておくといいかもしれませんね。
再利用する
1メソッドに1つの操作・役割ということを上手く利用すると、コードの重複を防ぐことにもつながります。
例えば、以下の部分を見てみましょう。
def 解答・解説のチェック
過去問の答えを用意する
答え合わせをする
間違えた問題にチェックを入れる
間違えた問題の解説を読む
解説のポイントをノートにまとめる
def テキストを用いた復習
テキストを確認する
テキストで間違えた問題の関連事項を確認する
関連するポイントをノートにまとめる
それぞれを細かく分けてみると以下のようになります。
def 過去問の答えを用意する
本棚を確認する
過去問の答えを探す
def 答え合わせをする
問題数分以下を繰り返す
答えと自分の回答を比較する
同じなら○、間違っていたら×をつける
def 間違えた問題にチェックを入れる
問題数分以下を繰り返す
答え合わせの結果を見る
×がついていたら問題にチェックを入れる
def 間違えた問題の解説を読む
問題数分以下を繰り返す
問題にチェックが入っているかを見る
入っていたら、解説を読む
ポイントを記録しておく
def 解説のポイントをノートにまとめる
ノートの空いているページを開く
記録されたポイントの分以下を繰り返す
タイトルを付ける
ポイントを書き記す
def テキストで間違えた問題の関連事項を確認する
間違えた問題の数分以下を繰り返す
テキストの関連するページを開く
関連事項を確認する
間違いに関連するポイントを記録しておく
def 関連するポイントをノートにまとめる
ノートの空いているページを開く
記録された間違いに関連するポイントの分以下を繰り返す
タイトルを付ける
ポイントを書き記す
「まとめる」という共通する動作を持つ部分がありますね。そこに注目しましょう。やっていることは同じで、何をまとめるのかだけが異なります。なので、以下のようにしてみましょう。
def ノートにまとめる(まとめるもの)
ノートの空いているページを開く
まとめるものの数分以下を繰り返す
タイトルを付ける
ポイントを書き記す
引数を利用して内容を指定することで、「まとめる」という動作を1つのメソッドに集中させることができました。
また、分ける前の本当の役割を果たす「メソッドをつなぐメソッド」は以下のように作れます。
def 解答・解説のチェック
過去問の答えを用意する()
答え合わせをする()
間違えた問題にチェックを入れる()
間違えた問題の解説を読む()
ノートにまとめる(解説のポイント)
def テキストを用いた復習
テキストで間違えた問題の関連事項を確認する()
ノートにまとめる(間違いに関連するポイント)
引数を指定して、「ノートにまとめる」という同じメソッドを条件を変えながら使いまわせています。
基本的に同じことを書くことはコードの可読性やメンテナンス性を下げます。細かい部品の内容を定義するのは一回で、部品をどう使うかを細かく変えるようにすれば、読みやすく修正しやすいコードになります。
また、長いメソッドを切り分ける過程で、別の所で使っていた小部品を使いまわせた、なんてこともあると思います。
「繰り返しを無くす」ことは見やすいコードを作るうえで非常に重要な要素です。一度きりで終わらせず、何度も使えるようなメソッドを作る事は特に意識したいですね。
命名
メソッドを定義するときはその名前にも十分に気を付けることが必要です。
メソッドの名前はそのメソッドの顔となります。読んですぐにメソッドの概要がわかることが理想です。
この記事の例では関数名に日本語を用いていますが、実際は英語を使うことがメインになると思います。ですが、まず日本語で命名し、それを英訳して考えるとかでも良いと思います。
メソッドに役割を詰め込み過ぎると命名に困ったり名前が長くなってしまうことがあるので注意しなくてはならないです。
また、アンチパターンを避けることも大切です。dataやgetなどの漠然とした単語は避け、具体的な単語を使うと良いらしいです。
ただ、あまり知られていない難しい単語を用いると他の人に意味が分かってもらえないので、多少長くなっても簡単な英語にする、という細かい調整も大切です。
おわりに
プログラムを作る時は以下のようにするとメソッドを有効活用することができると思います。
- とりあえず上から書いていく
- 処理の流れを見てグループ分けする
- グループごとにメソッドにまとめる
- それぞれのメソッドを1メソッドにつき1つの操作・役割になるように分ける
- 細かく分割したメソッドをまとめるメソッドを作る
- メソッド呼び出し部分を整える
分かりやすいプログラムを作るうえでメソッドの定義は避けては通れない道です。
可読性の観点やテストの観点は一人でやっているとなかなか気づけないことなので、バイトを通じて他の人のレビューをいただけたのは大きかったと思います。
有名な書籍『リーダブルコード』にもこういった心構えが書いてあります。自分も復習しておこうと思います。
そして、実際には引数などの要素も多分に関わってきます。今回は少ししか考えませんでしたが、それらが絡んでくるとむしろメソッド分けしない方が見やすい、使いやすい、なんてこともあるかもしれません。メソッド分けの世界は奥深いです。日々練習あるのみですね。
それはまた、明日の記事でお会いしましょう!