オブジェクト指向とCRUDの実装 #階層型オブジェクト
オブジェクト指向
Arrayクラスのように複数の要素をとるオブジェクトを考える
例
- users
問題
- user.delete()をしたときに、usersの配列を操作しなければならない
useStateなどを使ってる際にこの問題に直面。usersの長さが変わらないじゃないか。
考察
- つまり、deleteメソッドはusersから呼ばなければならない
- でも、updateはuser.update()で間違いない
users.delete(user);
user.update({ name });
まとめ
deleteを実装する時はcreateと同様、
usersにメソッドを生やさないといけない。
厳しい要件。だけどよくある要件。
- AがBを作成する
- AがBを削除する
- AがBをお気に入りにいれる
- AがBをお気に入りから削除する
- AがBを、Cというリストに追加する
- AがBを、Cというリストから削除する
- AがBを、Cというリストに(いつ, どこで)追加する
- AがBを、Cというリストから(いつ, どこで)削除する
このような要件に耐久性のある変数名を考える。
jQueryやGoogleのフレームワークはどうなのか?
.prepend()
.append()
.prependTo()
.appendTo()
.wrap()
.wrapAll()
Google.Firestore
ベストプラクティス
set, get, update, delete
user.setFollowing(user_id)
user.setList(list_name)
user.deleteList(list_id)
user.setUserToList(user_id, list_id)
user.deleteUserFromList(user_id, list_id)
jQueryのように単数でも複数でも同じメソッド名で処理。
モジュール側がブラックボックス化してその複雑性を吸収する。
user.deleteFollowing(user_id)
user.deleteFollowing([user_id])
const { user, getUser, deleteUser } = useUsers(user_id);
const { users, getUsers, deleteUsers } = useUsers();
継承と階層は違う
Q. 要件: UserにはMediaをもっており、MediaはCommentを持っている
A
- x: CommentはMediaから継承し、MediaはUserから継承しても良い
- o: メソッドやプロパティに共通性がない場合は別のものとして扱う
では、どういったものが継承できるか?
User
- Mine // 自身のアカウント。 .edit() など高次元のメソッドをもたせることができる
- Admin // 管理者ユーザー
mixinと継承の違い
mixin
- コンストラクタをもっていない
- ゆえに固有の情報をもてない
- 高次元の機能の提供
継承
- コンストラクタをもっている
- ゆえにインスタンス生成時にアイデンティティをもたせることができる
- 低次元(=ベースとなる, 基本的な)の機能の提供
【問一】以下それぞれ、mixinで実装すべきか継承すべきか答えなさい
- User
- Amin
- Car
- SoccerPlayerの能力
- MarjunPlayerの能力
- カーナビの機能
- 自動運転機能
- 電気自動車
API(CRUD)を持ったCollection。これはmixin?継承?
とった考察の順番
1. 大元のUserにmixinする // constructorがあるのでなし
2. Followings, Listsにmixinする // constructorがあるのでなし
3. Followings, Listsに継承させる
4. User, Followings, Lists, Users/Medias/Commentsに継承させる // OK
メソッド名
迷ったときはjQueryを参考にせよ
$(A).appendTo(B)
$(A).follow(B) // AがBをフォローする
主語.動詞(目的語)
単数と複数
jQueryは単数でも基本的に複数として扱っている
const users = new Users();
const user = new Users(id); // 単数
const followings = new Followings(user);
const following = new Followings(user, id); // 単数
const comments = new Comments(user, media);
const comment = new Comments(user, media, id); // 単数
階層型オブジェクト
オブジェクト指向とメソッドチェーンをかけあわせたもの。(Firestoreのサブコレクションのようなドキュメント型データベースに最適)
階層構造でエンティティを表現できる 【拡張性】 【可読性】
- 例
users[].followers[]
- 例
users[].medias[].likes[]
- 例
users[].medias[].comments[]
どんなメソッドの形がシンプルかな?
要件: 「ユーザーAがユーザーBをフォローする」
A. userA.followings.push(userB.uid);
B. userA.followings.set(userB.uid).set(userC.uid).push(); // バッジ処理可能な形
C. userA.setFollowing(userB.uid);
D. setFollowing(userA.uid, userB.uid); // 引数がわかりにくい。論外。
- A: オブジェクト指向
- B: バッジ処理可能なオブジェクト指向, メソッドチェーン
- C: 関数型, ツリー構造がもてない(後述)
- D: 論外。 .setFollowingでも.followでも何でもいいが論外。
主語.動詞(目的語)
の形をとることで:
- 引数が明示的になる
- メソッド名がシンプルになりポリモーフィズムとの相性が非常に良い
さらには汎用性, 拡張性, 可読性がある。次をみていこう。
子孫エンティティを持っても平気
オブジェクト指向の形をとれば子孫エンティティを持っても、全然その要件に対応できる。
要件: 「follwingsの配下にmediasがあり、さらにmediaは固有のプロパティを保持している」
// オブジェクト指向:
user.followings[n].medias[n].createdDate; // OK
user.followings[n].medias[n].remove(); // OK
user.followings[n].medias[n].likes[]; // OK
user.followings[n].medias[n].likes[].push({ user[, createdAt] }); // OK
// △
user.followings[n].medias[n].likeBy(user); // NG
user.followings[n].medias[n].likes[].pushBy(user); // △
// 関数型:
user.????(????) // ????
主語.主語.主語.動詞
や 主語.主語.主語.名詞
の形をとることで:
- かんたんに階層構造を表現
- このようなツリー構造は子孫エンティティを平気で扱うことができ
- 誰が見ても読みやすい
- 使い回すことも容易
- 拡張もかんたん。ハッピーターン
メソッド名が非常にシンプルになる
階層型オブジェクトで書くとメソッド名が非常にシンプルになるため、ポリモーフィズムとの相性バツグンです。
users[n].getIsAlreadyFollowed(userId)
↓
users[n].follwings.has(userId)
users[n].follwings.get()
users[n].follwings[n].get()
users[n].follwings[n].update(...)
まとめ
jQueryから発想を得た「階層型オブジェクト」は神✨
答え
【答一】
- User: ベースとなる継承元クラス
- Amin: Userからの継承したクラス
- Car: ベースとなる継承元クラス
- SoccerPlayerの能力: mixin
- MarjunPlayerの能力: mixin
- カーナビの機能: mixin
- 自動運転機能: mixin
- 電気自動車: Carからの継承したクラス。「電気で走る機能」ならmixinで良いだろう
Discussion