1. はじめに
会社で第3回の勉強会を主催したので学習内容のメモとなります。
名前設計に関する勉強会を開きました。
2. 概要
今回のテーマは、名前設計です。
今回の記事は、ECサイトとRPGを例に内容が進みます。
※解釈がずれている箇所がありましたら教えていただければと思います。
参考資料[1]:「良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方」
3. 内容:良くない命名/良い命名
凄腕エンジニア:設計書をみれば今後炎上するかしないかがわかるよ
3.1. 良くない命名
ECサイトを例に、商品といってもユースケースはいろいろある。
商品クラスに関心事が集まり肥大化、変更の影響範囲が広い。
注文ロジックで炎上したので鎮火する。
注文ロジックは鎮火に成功したが他に影響がでてないか確認が必要。
凄腕エンジニアはこれが予想できるらしい。
3.2. 良い命名
商品クラスをサービスごとに分けると仕様変更などの影響範囲が低減。
4. 命名のポイント
関心を分離し、ビジネス目的に沿った名前を付与 → 疎結合高凝集
- 可能な限り具体的に、意味範囲を狭く、特化した名前
- 存在ベースではなく、目的ベース
- 疎結合高凝集
- 違う名前に置き換えられないか検討
5. 命名の注意点
- 仕様変更時の「意味範囲の変化」に警戒
- 状況によって意味や扱いが異なる
- 名前的に居場所が不自然なメソッド
- その他
5.1. 仕様変更時の「意味範囲の変化」に警戒
開発初期では注文を「商品クラス」のみで扱っていたが実は注文には予約機能も含まれていた。
開発初期:注文機能のみ
仕様変更時:注文+予約機能
→ 足していくだけだと商品クラスみたいになる、分けられるときに分けよう
5.2. 状況によって意味や扱いが異なる
配送ロジック:配送先、配送元、などが商品について回る
注文ロジック:価格、レビューなどが商品について回る
→ 配送段階で価格やレビューは不要なので分ける(ドメインによる)
5.3. 名前的に居場所が不自然なメソッド
ここだけRPGを例に話を進めます。
勇者の攻撃でエネミーがアイテムをドロップしたとします。
ドロップしたアイテムをアイテムボックスに入れるのは誰の責務でしょうか?
敵も魔法を使うかもしれないのでconsumeMagicPoint
はOK。
敵に関するロジックなのに主人公の操作?
addItemToParty
これが危ないです!!
class Enemy {
int magicPoint;
Item dropItem;
// 魔法力を消費する
void consumeMagicPoint(int costMagicPoint) {
}
// 主人公のパーティにアイテムを追加する
boolean addItemToParty(List<Item> items) {
if(items.size() < 99) {
items.add(dropItem);
return true;
}
return false;
}
}
RPGにおいてアイテムを取得するタイミングは敵だけじゃない。
上のコードだと付与する役割が増えるたびに重複コードが増える。
「動詞+目的語」のメソッドに対する改善策
目的語の概念を表すクラスを作る。
そのクラスに、動詞1語のメソッドを追加する。
5.4. その他
# | ポイント | 概要 |
---|---|---|
1 | ロジック構造をなぞった名前 | isMemberHpMoreThanZeroAndIsMpMoreThanMagicCost コードをみればわかるような名前はやめよう |
2 | 技術駆動命名 |
~~Function cacheなど予約語みたいなのを使わない |
3 | 驚き最小の原則 |
メソッド名以上の機能は書かない |
4 | 驚き最小の原則 |
~Info ~Dataなどデータのみを連想させる名前はだめ |
5 | クラスが巨大化する名前 |
~Managerなどいろいろ あとさっき話したやつ |
6 | 意図がわからない名前 |
i, j, data, tmp, method1, method2 |
6. まとめ
今回のゴール:クラスとか関数命名の考え方を知る
関心を分離し、ビジネス目的に沿った名前を付与
重要なポイントがいくつかある
- 可能な限り具体的に、意味範囲を狭く、特化した名前
- 存在ベースではなく、目的ベース
- 疎結合高凝集
- 違う名前に置き換えられないか検討
7. 参考文献
- 良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方