会話形式で綴る「説明変数と要約変数」
登場人物
ワイ
- 41歳プログラマー
後輩くん
- ワイの後輩(20代)
- 大きなタスクを分割して、ワイに指示をしてくれる
とあるシステム開発会社にて
ワイ「あ〜〜〜っ!」
ワイ「ワイの実装したバックエンドがバグってたせいで」
ワイ「大事な社員名簿システムのデータが消えてもうてるやん!」
ワイ「これじゃ、バックエンドやなくてバッドエンドやないけ!コラァ!」
後輩くん「(なんでキレてんねん...お前のせいやろ...)」
後輩くん「せ、先輩、大丈夫ですか?」
ワイ「ぐぬぬ・・・取り乱してごめんやで」
後輩くん「先輩、気を取り直して行きましょう」
後輩くん「そのバッドエンド・・・じゃなくてバックエンドは僕が直しておきますから」
後輩くん「今日は一緒に、社員名簿システムのフロントエンド部分の実装を進めましょう!」
ワイ「せやな!」
後輩くん「今日はプロフィール編集ボタンを実装して行きましょう」
後輩くん「現状、ここまで出来てます!」
見た目
コード
<Text fw="bold">氏名:無職やめ太郎</Text>
<Button>プロフィールを編集</Button>
ワイ「おぉ、見た目は大体できとるな」
ワイ「そんで、今日は何をするんやっけ?」
後輩くん「今日は、このボタンを──」
- 社員本人
- 管理ユーザー
後輩くん「↑この人たちしか押せないように権限管理したいんです」
ワイ「なるほどな」
ワイ「他人のプロフィールを勝手に変えられんように、ってことやな」
後輩くん「はい!」
ワイ「ほな、こうやな!」
<Button
+ disabled={!(loginUser.id === thisUser.id || loginUser.roles.includes(1))}
>
プロフィールを編集
</Button>
ワイ「画面で確認してみると・・・」
ワイ「よっしゃ、ちゃんとdisabled
になっとるわ!」
後輩くん「先輩、マジでやめてください」
ワイ「ファッ!?」
後輩くんの指摘①「もっと説明してほしい」
後輩くん「条件が長すぎて読みづらいですし」
後輩くん「いきなりloginUser.roles.includes(1)
とか書かれても」
後輩くん「わけ分かんないっす」
ワイ「いや、これは──」
- ログイン中のユーザーが、管理ユーザーの役割(
1
)を持っている
ワイ「っていう意味やないか!」
後輩くん「じゃあ、それをちゃんと説明するコード書いてくださいよ」
後輩くん「後から読んだ人が困らないように!」
ワイ「むう、分かったやで」
<Button
+ // ログイン中のユーザーが、管理ユーザーの役割(1)を持っていること!
disabled={!(loginUser.id === thisUser.id || loginUser.roles.includes(1))}
>
プロフィールを編集
</Button>
ワイ「↑こうやな!」
後輩くん「いやぁ、その説明コメントでもいいんですけど」
後輩くん「これくらいならコードだけで説明できそうなので」
後輩くん「説明変数を書きましょうよ」
ワイ「説明変数・・・?」
後輩くん「はい。つまり──」
const isAdminUser = loginUser.roles.includes(1);
後輩くん「↑こうしましょう」
ワイ「なるほど」
ワイ「パッと見で何か分からん値には、説明のために名前をつけてあげるんやな」
後輩くん「そうです!」
後輩くん「あと1
っていう値もそのままじゃ分からないので」
const USER_ROLE_MAP = {
ADMIN: 1,
MEMBER: 2,
} as const;
後輩くん「↑こう、名前をつけてEnum
っぽく管理してあげて」
const isAdminUser = loginUser.roles.includes(USER_ROLE_MAP.ADMIN);
後輩くん「↑こう使ってあげましょう」
ワイ「ふみふむ」
後輩くん「1
とか2
とかだと」
後輩くん「後からコードを読んだ人が──」
「なんやこの不思議な数字は?」
後輩くん「って思っちゃいますからね」
後輩くん「こういうのはマジックナンバーって言って、アンチパターンなんです」
ワイ「おお〜、了解やで!」
もう1つの条件も
後輩くん「あと、loginUser.id === thisUser.id
っていう条件は」
後輩くん「自分自身のプロフィールかどうか、って意味だと思うんですけど」
ワイ「そうやでぇ」
ワイ「こっちはちゃんとloginUser
とかthisUser
とか」
ワイ「値の意味が分かるような名前がついとるから、問題ないやろ!」
ワイ「これ以上、説明変数にすることもできんやん!」
後輩くん「そうですね・・・でも・・・」
後輩くんの指摘②「もっと要約してほしい」
後輩くん「loginUser.id === thisUser.id
も」
後輩くん「まだ要約できる余地があると思うんです」
ワイ「要約ぅ・・・?」
後輩くん「はい」
const isOwnProfile = loginUser.id === thisUser.id;
後輩くん「↑こうですね!」
ワイ「なるほどなぁ」
ワイ「loginUser
とかthisUser
とか」
ワイ「意味が分かる名前たちによって構成されている式でも」
ワイ「さらに要約して明記してあげることもできるんやな」
後輩くん「そうですね」
後輩くん「こういうのは要約変数と呼ばれます」
ワイ「要約変数かぁ」
ワイ「おおきに!勉強になったやで!」
ワイ「ほな──」
<Button disabled={!(isOwnProfile || isAdminUser)}>
プロフィールを編集
</Button>
ワイ「↑これで完成やな!」
後輩くん「あと少しだけ要約しましょう!」
const canEditProfile = isOwnProfile || isAdminUser;
後輩くん「↑こうして──」
<Button disabled={!canEditProfile}>
プロフィールを編集
</Button>
後輩くん「↑こうですね」
ワイ「なるほどぉ。つまり──」
要するに「プロフィールを編集できる」ってのは
自分自身のプロフィールである場合、もしくは管理ユーザーでログインしている場合やで!
ワイ「↑こんな風に要約して明記してあげるんやな」
後輩くん「その通りです!」
ワイ「こう、ちゃんと命名されて明記されてると──」
編集できない場合は、
disabled
になるんやで!
ワイ「↑って、当たり前なくらい読みやすくてええな!」
後輩くん「そうですね、わざわざ命名した方が」
後輩くん「条件ベタ書きよりも意図が伝わると思います」
ワイ「コード書いてるワイ本人も、混乱しにくくて良さそうやわ」
ツールチップも表示したい
後輩くん「権限がなくてボタンが押せない場合は」
後輩くん「マウスをホバーさせた時に、その理由も表示させてあげましょうか」
後輩くん「デザインで言うと──」
デザイン
後輩くん「↑こんな感じです」
ワイ「おお、ええ感じやな」
ワイ「こう表示されていれば、管理者に問い合わせたりできるもんな」
後輩「はい」
ワイ「ほな、コードとしては──」
+ <Tooltip label="本人または管理ユーザーだけが編集できます">
<Button disabled={!canEditProfile}>
プロフィールを編集
</Button>
+ </Tooltip>
ワイ「↑こうやな!」
ワイ「これで、ホバー時にツールチップが表示されるわ!」
後輩くん「でも、これだと」
後輩くん「ボタンが押せる場合でもツールチップが表示されちゃいますよ」
ワイ「あぁ、そうやな」
ワイ「ほなTooltip
コンポーネントに」
ワイ「disabled
っていうprops
を渡してやらんとアカンな」
ワイ「ボタンを押せる場合には、ツールチップは非表示なわけやから・・・」
ワイ「あれ、頭こんがらがってきたけど──」
<Tooltip
label="本人または管理ユーザーだけが編集できます"
+ disabled={canEditProfile}
>
<Button disabled={!canEditProfile}>
プロフィールを編集
</Button>
</Tooltip>
ワイ「↑こうやな!」
後輩くん「そうですね」
ワイ「でも、なんか・・・」
disabled={canEditProfile}
ワイ「↑この部分が・・・」
ワイ「disabled
っていう名前のprops
なのに」
ワイ「canEditProfile
っていう肯定形の変数がそのまま渡されてて」
ワイ「なんか・・・」
「無効なのか、できるのか、どっちやねん!」
ワイ「って気がしてしまう・・・!」
ワイ「それに・・・」
ボタンを押せる場合には、ツールチップは非表示なわけやから
ワイ「↑これが頭こんがらがるから、もっと説明したい・・・」
ワイ「・・・せや!」
const isTooltipDisabled = canEditProfile;
ワイ「↑こう明確に書いてあげよ!」
後輩くん「なるほど」
ボタンが押せるとき、ツールチップは見えないよ!
後輩くん「っていうことを明記してあげるんですね!」
ワイ「せや!」
ワイ「そんで──」
<Tooltip
label="本人または管理ユーザーだけが編集できます"
- disabled={canEditProfile}
+ disabled={isTooltipDisabled}
>
<Button disabled={!canEditProfile}>
プロフィールを編集
</Button>
</Tooltip>
ワイ「こうやな!」
後輩くん「おお〜」
後輩くん「先輩から能動的に説明・要約しに行くなんて」
後輩くん「素晴らしいです!」
ワイ「ガーファファファ!そうでもないでぇ!」
まとめ
- 後から読む人にとって意味が分かりにくい値
- 例:
loginUser.roles.includes(1)
など- 変数名をつけることで、説明してあげよう
- 例:
- 分かりやすい名前がついた変数によって構成されている式でも、まだ認知負荷を減らせそうな場合
- さらにまとめて変数名をつけることで、要約してあげよう
- わざわざ命名して明記しながら進む方が、書いてる本人も混乱しづらい
ワイ「↑っていうことやな!」
後輩くん「そうですね!」
〜完〜
※この記事は、書籍「リーダブルコード」を参考に書きました
Discussion