2023年明けましておめでとうございます!Czです!
今年は「大吉」だったので自分にしかできない「なにか」を見つけるため色々チャレンジしていきたいと思います!
ということで、
今回はプログラミングにおける命名がうまくいくコツを書いてみたいと思います!
対象読者は以下の通り
- 名前付けが苦手な人
- プログラミングに慣れてきた初級〜中級者
※できるだけ難しい言葉を使わないようにしています。
皆さんはクラス名やメソッド名、変数名などの名前付けは得意ですか?
正直僕はあまり得意ではありません(でした)。
「とにかく動くものをはやく作りたい」
その気持ちだけが先行してしまい、行き当たりばったりで適当な名前を付けて実装。
後々見返してみると、
「あれ?この処理なんだっけ?」
とか、
「名前と実装が合ってないじゃん!」
とか。
経験ありますよね。
一人で開発しているならまだしも、チームで開発している場合は周りのメンバーにも迷惑をかけてしまいます。
プログラミング時間の7割以上はコードを読む時間と言っても過言ではありません。
名前が適当なために多くの時間をプログラムの理解に費やすのはもったいないですね。
そうならないためにも、本記事を見て可読性の高い名前付けができるように一緒に学んでいきましょう!
一度に全部伝えるのは難しいので、まずは簡単な分類から行なっていきましょう!
※本記事は一部PHPで記述されています。他言語の方は適宜読み替えてください。
「もの」「こと」「状態」「イベント」の4つの性質に分類しよう
まず始めに、コードで実現したいことや、そこに出てくる登場人物たちを「もの」「こと」「状態」「イベント」の4つのいずれかに分類してみましょう。
例えば以下のような仕様があったとします。
操作者が登録ボタンをクリックしたら、ユーザーが登録済みかどうかを判定し、登録されていなかったら登録する。
ここに出てくる単語をこんな風に分けてみましょう。
分類ルールを簡単に説明すると
もの
→ 名詞
こと
→ 「〜すること」で終わらせることができる動詞
状態
→ 「〜かどうか」で終わらせることができる単語
イベント
→ 「〜された( or した)」で終わらせることができる動詞
となります。
一通り分けられたなーと思ったら次に進みましょう。
「もの」「こと」「状態」「イベント」の関連を理解しよう
「もの」を基点とした「もの」「こと」「状態」「イベント」の性質を理解してみます。
「もの」は主語になりうる
「もの」は別の「もの」を持つことができる
「もの」は「こと」の目的語になりうる
「もの」は「イベント」を発生させることができる
「もの」は「イベント」を検知することができる
「もの」は「状態」をもつ
「もの」は「こと」を実行できる
このように「もの」を基点に「もの」「こと」「状態」「イベント」が関わってきているのです。
「もの」は重要なんですね。
では次に「もの」「こと」「状態」「イベント」を使って短い文章を組み立てていきます。
文法は次の通り。
- 「もの」が「もの」を「こと」する
- 「もの」は「状態」である
- 「もの」が「イベント」された
- 「もの」が「もの」を「イベント」した
使用する単語は以下から選びます。
- 登録ボタン
- ユーザー
- 登録(する)
- 登録済み(かどうか)
- クリック(された)
では作ってみましょう。
- 「?」が「ユーザー」を「登録」する
- 「ユーザー」は「登録済み」である
- 「登録ボタン」が「クリック」された
- 「?」が「ユーザー」を「登録」した
おっと、1と4の主語が足りないことに気づきましたね。
日本語はしばしば主語を省略することがあります。
なので重要な主語が抜けていることに設計の段階では気づきにくく、いざ実装を始めた時にどうしようと悩むことが多々あります。
このタイミングで気付けて良かったです。
ではこの「?」に当てはまる主語の名前を決めていきましょう。
「誰が」ユーザーを登録できますかね〜(シンキングタイム)
...
はい、Aさん!回答をどうぞ!
Aさん「(ごにょごにょ)」
え〜っと「私」ですか...?
「私」が「ユーザー」を「登録」する
確かにあなたが作ったプログラムで「ユーザーを登録する」から主語が「私」でもいいかもしれませんね。
ただし残念ながら「私」は却下でお願いします。
理由は、プログラムは1年365日24時間休みなく稼働することができます。 あなたがそこに入ってしまうと過労で壊れてしまうかもしれません。
可用性の観点から考えるとやめた方が良いですね。
他に案はありますか?
はい、Bさん!
Bさん「(ごにょごにょ)」
なるほど「システム」ですか。
「システム」が「ユーザー」を「登録」する
間違ってはないですね。
でも「システム」はちょっと広すぎるかもしれませんね。 なんでも「システム」に任せてしまって肥大化し、メンテナンスできなくなる未来が見えます。
そろそろ時間切れということで回答を締め切ります。
「?」に当てはまる名前は〜...「ユーザー登録サービス」とします。
(ちょっとくどい気もしますが主語の責務がはっきりしているため良しとします)
「?」の名前が決まったので再度先ほどの文章を確認してみましょう。
- 「ユーザー登録サービス」が「ユーザー」を「登録」する
- 「ユーザー」は「登録済み」である
- 「登録ボタン」が「クリック」された
- 「ユーザー登録サービス」が「ユーザー」を「登録」した
文章として問題なければここまではOKです。
NG例
次の文章は良くありません。なぜなら主語の役割ではないからです。
- 「登録ボタン」が「ユーザー」を「登録」する
- 「ユーザー登録サービス」が「クリック」された
日本語では違和感あるのに何故かプログラム中ではこういったものが散見されます。
プログラムに落とし込もう
ここまでで単語を4つの性質に分類し、違和感の無い文章が組み立てられることを確認しました。
ここからは実際に「クラス名」「変数名(メンバ変数)」「メソッド名」に変換していきましょう。
次の図を見てください。
「クラス」に対しては「もの」の名前しか付けられないように、名前をつけたい対象によって付けられる性質が限られてきます。
これらのパターンに沿って名前をつけていくことで迷うことなく分かりやすい名前をつけることができるのです。
メソッド × こと
クラスはインスタンス化することができ、主語や目的語になるものです。
そのためクラス名には「もの」となる名前(名詞)を指定しましょう。
そのまま翻訳
そのまま英語に翻訳すればOKのパターン
意味 | クラス名 | ポイント |
---|---|---|
ユーザー | User | そのまま名詞となる英語に翻訳 |
企業 | Client | そのまま名詞となる英語に翻訳 |
動詞から始まらないように注意
「ユーザーを登録するサービス」をクラス名にしてみましょう。
よくやってしまうのが「ユーザーを登録する」をそのまま翻訳してRegisterUserService
としてしまうことです。
一見問題なさそうに見えますが、これを翻訳すると「ユーザーサービスを登録する」となってしまいます。
つまりRegister
の目的語がUserService
となってしまったということですね。
※英語は「配置のことば」と呼ばれるほど単語の順番が重要なのです。
こうならないためのコツとして、「ユーザーを登録する」という動作を一度「ユーザー登録」という名詞に変換してから翻訳してみましょう。
するとUserRegistration
になりますね。
あとはこれにService
をつけてあげれば出来上がりです!
意味 | クラス名 | ポイント |
---|---|---|
ユーザー登録サービス | UserRegistrationService | ・動詞になっていないか注意する |
英和辞典などのサイトで単語を検索すると、「名詞」「動詞」「形容詞」などが書かれているかと思います。
例えば先ほどのRegister
ですが、実は名詞としても使えます。
ただし意味は
登録簿,登記簿; (特定の人の)名簿.
となっており、期待する意味とはちょっと違いますね。
Registration
の場合、登録という行為自体を表しているためこちらの方がニュアンスが近そうです。
このように日本語を英語に翻訳する際は翻訳サイトだけではなく、英和辞典などを使って単語の使われ方が正しいかどうかまでチェックすると、より適切で洗練された名前をつけることができるようになります。
メンバ変数 × もの
メンバー変数はクラスに付随する情報です。
「もの」かどうかの判別
メンバ変数になりうる性質に「状態」というものもあります。
状態とものの区別の仕方は次の通りです。
- クラス(主語)を説明しているものは「状態」
- クラス(主語)を説明していないものは「もの」
こんな風に覚えると良いでしょう。
「〇〇 は 〇〇」に当てはまるものは「状態」
「〇〇 の 〇〇」に当てはまるものは「もの」
例えば「名前」の場合、
「ユーザー は 名前」 だとおかしいですが
「ユーザー の 名前」 だとしっくりきますね
なので「名前」は「もの」になります。
「登録済み」ではどうでしょう。
「ユーザー は 登録済み」
「ユーザー の 登録済み」
前者の方がしっくりきますね。ということは「登録済み」は「状態」になります。
状態といえば「ステータス」はどちらでしょうか?
「ユーザー は ステータス」
「ユーザー の ステータス」
今度は後者の方がしっくりきます。なので「ステータス」は「もの」になります。(面白い)
意味 | 変数名 | ポイント |
---|---|---|
名前 | $name | ・そのまま名詞となる英語に翻訳 |
住所 | $address | ・そのまま名詞となる英語に翻訳 |
ステータス | $status | ・そのまま名詞となる英語に翻訳 ・「状態」という単語は「もの 」 |
メンバ変数 × 状態
上で説明した通り、クラス(主語)を説明しているものは「状態」となります。 ここでは状態を表す変数名を作っていきましょう。
「is」から始まる理由
「〇〇 は 〇〇」に当てはまるものは「状態」と伝えました。
これは英語に訳すと「〇〇 is 〇〇」となります。
論理値(boolean)ではis
をつけましょうというルールをよく耳にしますね。
あれはis
をつけることで主語となるインスタンスの状態を説明する形にできるからなんです。
User is active → $isActive
is
は三単現(三人称、単数、現在形)の時のみ使えるbe動詞です。
でも正直プログラム中ではあまり気にする必要はありません。
なぜなら、クラスのインスタンスから見たときの状態とは大体三単現に当てはまるからです。
(だから論理値はisをつけておけば良いって広まったのかもしれないですね)
意味 | 変数名 | ポイント |
---|---|---|
(ユーザーは)アクティブです | $isActive | ・isをつけて主語であるユーザーの状態を説明 |
持っているという状態
「〇〇 は 〇〇 を持っている」という形です。
英語に訳すと「〇〇 has 〇〇」となります(hasも三単現ですね)
例えば「スキルを持っている」という状態を変数にしたい時、何も考えずに「論理値だからisをつけるか〜」としてしまうと次のようになってしまいます。
User is skill
これだと「ユーザーはスキルです」という意味になってしまいます。
プログラム上だと文脈や使われ方から言いたいことは分からなくもないですが、なかなか脳への負担が大きいです。
ここは意味が通るように
User has skill → $hasSkill
としてあげるのが良いでしょう。
意味 | 変数名 | ポイント |
---|---|---|
(ユーザーは)スキルを持っています | $hasSkill | ・hasを使って「持っている」という状態を説明 |
受動態は主語の状態を表す
もう一つよく使われるのが、「〜された」とか「〜済み」とかを表す状態ですね。
「ユーザーは登録されている」を変換した場合、以下のようになります。
User is registered → $isRegistered
「be動詞 + 動詞の過去分詞」の形で翻訳することができます。
意味 | 変数名 | ポイント |
---|---|---|
(ユーザーは)登録済みです | $isRegistered | ・受動態 |
メソッド × こと
主語となるクラスが「できること」をメソッド名として定義します。
動詞から始める
メソッドは動作を伴う処理です。
なので動作となる名前を付けましょう。
例えば「名前を取得する」場合は
getName
「ユーザーを登録する」場合は
registerUser
このように「〜を」の部分が動詞の目的語となり、動詞の後ろにくっつきます。
動詞 + 目的語
意味 | メソッド名 | ポイント |
---|---|---|
名前を取得する | getName() | ・get + 目的語 |
ユーザーを登録する | registerUser() | ・register + 目的語 |
メールを送信する | sendMail() | ・send + 目的語 |
getを省略
get
は省略できることがあります。
例えば「ユーザーの名前を取得する」の場合、ユーザーがどこから名前を取得してくるかを考えます。
ユーザーのインスタンス自身が既に名前を生成するための情報を持っていて、都度外部(例えばDB)から情報を取得しないのであればメソッド名をname
としても問題ないです。
つまり、ユーザーのプロパティのように扱うということですね。
意味 | メソッド名 | ポイント |
---|---|---|
名前を取得する | name() | ・外部から情報を取得するような処理でなければgetを省略 |
主語と目的語が同じなら目的語を省略
目的語も省略できることがあります。
「キャッシュをリセットする」というメソッドを作りましょう。
そのまま翻訳するとresetCache
になりますね。
この動作をCache
クラスを持っている場合、メソッド名からCache
を消しても問題ありません。
なぜなら主語と目的語が同じ場合、読み手が受け取る情報量が変わらないからです。
なのでCache::reset()
とすることができます。
意味 | メソッド名 | ポイント |
---|---|---|
キャッシュをリセットする | Cache::reset() | ・「何を」に当たるCacheはクラス名にあるのでメソッド名から除外 |
目的語が明白なら目的語を省略
先ほどと同じように読み手が受け取る情報量が変わらないのならば目的語を省略しても良いでしょう。
例えば「ユーザー登録サービスがユーザーを登録する」という場合、「ユーザー登録サービス」が「ユーザー(目的語)」を登録するのは明白ですね。
なので次のようにメソッド名からUserを省略してしまいましょう。
UserRegistrationService::registerUser();
↓
UserRegistrationService::register();
意味 | メソッド名 | ポイント |
---|---|---|
ユーザー登録サービスがユーザーを登録する | UserRegistrationService::register() | ・目的語が明白なのでメソッド名から除外 |
目的語を引数にとる場合、目的語を省略
目的語を引数に取る場合、目的語を省略してもいいかもしれません。
例えば「オブジェクトを文字列に変換する」といういうメソッドの場合、convertObjectToString(object $object)
となりますが、引数でobject
を取っているので文字列に変換する対象がobject
なのは明白ですね。
なのでconvertToString(object $object)
としても意味が通じるかと思います。
意味 | メソッド名 | ポイント |
---|---|---|
オブジェクトを文字列に変換する | convertToString(object $object) | ・「何を」に当たるobjectは引数にあるのでメソッド名から除外 |
ただし、常に省略しても良いとは限りません。
省略しても意味が通じるか?情報量は減っていないか?を常に意識し、損なう場合は無理に省略しない方が良いでしょう。
メソッド × 状態
状態を返すメソッドを作成する場合、主語がどこにあるかを意識しましょう。
クラス自体が主語となるケース
基本的に「メンバ変数 × 状態」と同じです。
ただしメンバ変数との違いとして、何かしらの情報を使って計算していることに注意してください。
例えば「(ユーザーは)アクティブです」という状態を「ユーザーが削除されていないかどうか」で判断することがあると思います。
以下のような感じですね。
public function isActive(): bool { // 削除日時が設定されていなければアクティブとする return empty($this->deletedAt); }
また「ユーザーがスキルAを持っている」というメソッドがあるとして、「スキルA」に当たる部分を引数で受け取る場合もあると思います。
public function hasSkill(string $skill): bool { // ユーザーがスキルを持っているかどうか return in_array($skill, $this->skills, true); }
いずれにしても主語が「ユーザー」なのは変わりません。
意味 | メソッド名 | ポイント |
---|---|---|
(ユーザーは)アクティブです | $user->isActive() | ・アクティブかどうかを「削除されていないかどうか」で判断 ・主語は「ユーザー」 |
(ユーザーは)スキルを持っています | $user->hasSkill(string $skill) | ・「何を持っているか」を引数で受け取る ・主語は「ユーザー」 |
引数が主語となるケース
主語がクラス自体とならないケースも存在するので注意してください。
例えば「パラメータが有効か」というメソッドの場合、文章だけで判断すると主語は「パラメータ」になりますね。
しかし実際は「登録サービス」がこのメソッドを持つことがあるかもしれません。
するとこうなります。
class RegistrationService { public function isValid(array $params): bool { // パラメータが有効かどうか return true; } }
こういった実装はよくありますね。これ自体は悪いことではないです。
ここで考えて欲しいのが「主語がクラス自体でなくとも、クラスの責任の範疇となっているか(責務はあっているか)」ということ。
そうしないとこれらの処理は「ユーティリティ」クラスに書かれてしまい、ユーティリティクラス肥大化問題が発生してしまうかもしれません。
(Validatorクラスを作ったり、パラメータをクラス化し、そこにisValid()
を持たせるなど回避する方法はいろいろありますが今回は割愛します)
意味 | メソッド名 | ポイント |
---|---|---|
パラメータは有効です | RegistrationService::isValiid(array $params) | ・引数の$paramsが主語になっている |
わかりにくければ主語を明示しよう
先ほどのisValid()
ですが、やはり主語がわかりにくいですね。
こういった場合はメソッド名に主語を含めてみましょう。
意味 | メソッド名 | ポイント |
---|---|---|
パラメータは有効です | RegistrationService::paramsIsValiid(array $params) | ・主語をメソッド名に記述することで「何が」の部分をはっきりさせている |
メソッド × イベント
イベントは条件を満たした瞬間に発生します。
「いつ」「どんな時」に発生するのかを頭の中でイメージしましょう。
操作した時に発生するイベント
「ボタンを押した時」「文字を入力した時」など、誰かが操作した時に発生するイベントです。
ここで覚えておいて欲しいのは主語が「誰か」となり、自分(クラスのインスタンスや入力コンポーネント)自身ではないということです。
コツとしては先頭にon
をつけることで、発生した瞬間を表現することができます。
意味 | メソッド名 | ポイント |
---|---|---|
ボタンを押した | onClickButton() | ・on + 動詞の原形から始まる ・主語は「誰か」 ・動作が発生した瞬間を表す |
状態が変わった時に発生するイベント
上の例では「誰かがボタンを押した」となっていましたが、今度は「ボタン」側の視点で考えてみましょう。
文章にすると「ボタンが押された」となります。
するとどうでしょう。
先ほどまでは「イベント(こと)」だったのが「イベント(状態)」に変わりました。
つまり視点の変化で動作から状態に変えることができるということです。
ここでも先頭にon
をつけて、状態が変わった瞬間を表現しましょう。
意味 | メソッド名 | ポイント |
---|---|---|
ボタンが押された | onClickedButton() | ・on + 動詞の過去形から始まる ・主語は「ボタン」 ・状態が変わった瞬間を表す |
ちなみに状態ということは動作を伴わなくとも使えそうですね。
意味 | メソッド名 | ポイント |
---|---|---|
ユーザーが登録された | User::onRegistered() | ・on + 動詞の過去形から始まる ・主語は「ユーザー」 ・状態が変わった瞬間を表す |
登録処理が完了した | RegistrationService::onRegistrationCompleted() | ・on + 動詞の過去形から始まる ・主語は「登録処理」 ・状態が変わった瞬間を表す |
このような状態の変換を監視し、それをトリガーに別の処理を実行する実装パターンとして「オブザーバーパターン」というものがあります。
ここでは深く説明しませんが、状態の変化をイベントとして捉えることでコードの表現の幅が広がり、スマートに記述することができるようになるので知らなかった方は是非調べてみてください。
イベントにおける主語は自由にコントロールできるので、より分かりやすくなるように様々な視点から物事を捉えてみるのも良いかもしれません。
さいごに
今回は「もの」「こと」「状態」「イベント」という4つの性質を中心に捉え方を学んでいきました。
主語は何か?責務はあっているか?なども確認してきましたね。
これらの知識は、設計やアーキテクチャを学んでいく上でとても大事になると思います。
本当はもっと伝えたいことがあるのですが、それは次回以降にしたいと思います。
普段頭の中でイメージしていることを言語化するのはやはり難しいですね。
こういった話をもっとしたい、知りたいって方はコメントください。
(一緒に働くことになったらいっぱいお話しできそうですね!)
ということで次回もお楽しみに!
\\『真のユーザーファーストでマーケットを創造する』仲間を募集中です!! //