Posted at

Privateメソッドの必要性を理解して実装するための実践メモ


背景

2019年の4月から主にrubyで新機能開発を担当しています。

サービスリリース当初に実装され、その後ほとんど改修されずにいたレガシーコードを、3年の月日を経て改修しました。

その過程でメソッドを公開すべきか非公開にすべきかに関して色々と考えることがありました。

ここでは自分のための整理を兼ねてまとめようと思います。


概要

クラスは果たすべき1つの責務を持ちます(単一責務の原則)。

その責務については、クラスができること・何をするのかを外部に公開、宣言します。

しかしながらそれをどのように実現するのかの詳細は内部に留めておきます。

言葉ではわかっていても、実行するのはまた少し次元が違う話です。

そこで、実際に概念を使いこなすために重要だと感じたり、先輩エンジニアの方からいただいた教えを以下の3つに分けて整理します。


  1. クラスの責務を言語化する

  2. 最短で、負荷最小で解読できるか

  3. 仕様は変更され、コードは書き換えられる


クラスの責務を言語化する

クラスがなんのために存在しているのかを端的に言語化する必要があります。

その明文化を怠ると、色んなことができる(責務がブレている)クラスが出来上がったり、使う場所ごとに異なる要求が生じた際にまとめきれなくなります。

私の所属するプロジェクトでは、レコードの検索に Finderクラスを実装していますが、「誰のために(どんな用途のために)」「何を見つけてくれるのか」が不明確なクラスを作成してしまい、途中で大きめのリファクタリングが必要になってしまいました。

逆にこの言語化がバチっと決まると、


  • どんな要求に応える必要があるのか(=必要なメソッドの列挙)

  • どんなインターフェースを提供すれば良いか

  • 外部からは返り値に対してどんな期待がされるのか

というクラスに関する本質的な事項が明らかになってきます。

これらの情報が過不足なくまとめられていると、必要最小限ですっきりとしたクラスが実装できるはずです。

逆に上記の本質的な事項から外れた詳細はprivateメソッドとして実装します。

誰かに仕事を依頼する時には、何をしてほしいのかをイメージし、依頼内容をまとめ、成果物を受け取り・評価しますよね。

その依頼から仕事の完遂の間のプロセスは、依頼された人だけが知っていればいいのです。(カプセル化と言います)


最短で、負荷最小で解読できるか

過去のコードと対峙すると、なんとも理解しがたいファットメソッドに出会うこともあります。

わかりにくいコードは、例えば以下のような特徴があるように思います。


  • 結局何をやってくれるのかがコードから即座に読み取れない

  • なんのための処理なのか、処理のかたまりがどこまで続くのかが不鮮明

  • 定数名や関数名など、名前から明確な意味を感じ取れない

解決策としてコメントを書きまくるというのも考えられますが、

コメントを書く前にコメントがなくても理解できるように書けないかを考えるべきです。

(少し変わった実装をしないといけない事情や、伝わりにくい実装意図など、コメントを書くべきタイミングも勿論あります)

チーム開発では他のメンバーが自分のコードを読んで理解する時間も工数に含まれますから、

最短、最小負荷で理解できるコードを書くことの重要性は想像以上です。

そこで活躍するのもprivateメソッドです。

関数をいくつかの処理のブロックに分割し、1つ1つをprivateメソッドとして実装することで、

大元のメソッドを読む時には処理手順書を読むような形となります。

(その関数は大抵外部のクラスが知っている必要がないし、知ってほしくもないのでprivateになるというわけです)

各手順の詳細が知りたい時だけ、privateメソッドの中身を見ればいいのです。

「20行に渡ってごにょごにょしてるけど、結局これをやってるだけかい!」みたいなノリツッコミが起こらないよう、

初めから何をするか関数として宣言してあげましょうね。


仕様は変更され、コードは書き換えられる

納品で縁が切られるような製品でない限り、プログラムはいつか修正されることを前提とする必要があります。

これは自身がまっさらなところから機能を実装する場合にはあまり意識しないことでした。

私が担当した機能はシステムの根幹となる部分で、初期実装時から3年間手がつけられていませんでした。

しかし法改正に伴い前提が覆り、コードの変更を余儀なくされました。

改修の前に影響範囲を一通り調べる際、publicメソッドとprivateメソッドでは以下のような違いが出ます。

◆publicメソッド

外部に対して公開されている

⇨ 使われている全ての場所(クラス外部)に影響が生じ得る

⇨ 外部で使う必要がなさそうだと思っても、外部で使われている可能性を否定できない

◆privateメソッド

内部に閉じている

⇨ クラス内で使われている箇所だけに影響が生じる

⇨ 責務を果たすための詳細にすぎないので、影響が限定的

例えば、引数を追加したい、メソッド名を修正したい、という修正が必要になった場合、

privateメソッドの場合にはそのクラス内で使われている箇所だけを確認すればOKです。

しかし不必要に公開されてしまっている場合、どこで使われているのかを確認し、

漏れなく修正しきる必要が出てしまいます。

公開するメソッドを必要最小限にすることで、クラスが担う役割を明確にし、

かつ修正も容易になります。


参考

私の提出したPRにて引用されたプライベートメソッドに関するt_wadaさんの見解:https://qa.atmarkit.co.jp/q/2784