Mais conteúdo relacionado Semelhante a Datastore/Go のデータ設計と struct の振る舞いについて (14) Datastore/Go のデータ設計と struct の振る舞いについて9. type User struct {
//他のフィールドは省略
ProfileImage, ProfileMovie string
}
それぞれが単なるフィールドだと
「Image, Movie どちらか一方を登録する」
という仕様を表現するのは難しい
11. type User struct {
Profile Profile
}
type Profile struct {
Image, Movie string
}
func NewImageProfile(image string) Profile {
return Profile{
Image: image,
}
}
func NewMovieProfile(movie string) Profile {
return Profile{
Movie: movie,
}
}
「Image, Movie どちらか一方を登録する」
というルールを Profile 自体に持たせることで
仕様を表現することができる
15. type User struct {
TEL, Email string
}
func Get(scope string) User {
var u User = GetUserFromDB()
if scope != "Contact" {
u.TEL = ""
u.Email = ""
}
return u
}
ロジック上で Contact scope = Email, TEL を表現している
17. type User struct {
Contact Contact
}
type Contact struct {
TEL, Email string
}
func Get(scope string) User {
var u User = GetUserFromDB()
if scope != "Contact" {
u.Contact = nil
}
return u
}
Scope が扱う Contact という概念は
具体的に TEL, Email を含む
抽象度の違う値同士を扱おうとすると、
ロジックが複雑になる可能性がある
TEL, Email という具体的な概念を
Scope の Contact という抽象度に合わせる
Scope と struct が一致しているので、
直感的に理解しやすい
Contact の持つ値が変化しても、
u.Contact = nil に修正は発生しない
これはロジックが Contact という抽象度の概念
を扱っているからであって、
t.TEL = “” のように抽象度がマッチしない場合
に比べると変更に強くなる
21. type User struct {
HP, ATK, DEF int
}
func (u *User) GetAtk() int {
//HPに依存する
return u.ATK * u.HP
}
func (u *User) GetDef() int {
if u.HP < 100 {
//ピンチになると強くなる
return u.DEF * 2
}
return u.DEF
}
それぞれの値を算出するロジックは
HP, ATK, DEF に依存しているが、
他の値には依存していない
23. type User struct {
Battle Battle
}
type Battle struct {
HP, ATK, DEF int
}
func (b *Battle) GetAtk() int {
//HPに依存する
return b.ATK * b.HP
}
func (b *Battle) GetDef() int {
if b.HP < 100 {
//ピンチになると強くなる
return b.DEF * 2
}
return b.DEF
}
HP, ATK, DEF を Battle として定義
「対戦」に関するロジックは
Battle に集中させる
User は「対戦」以外のロジックに集中できる
対戦のロジックはゲームのコアな要素なので、
複雑な仕様になりやすい
User から分離しておくと
Battle に interface を持たせて
特定のロジックを抽象化させたり、
固定値を設定した Battle に差し替えるなど、
User を汚さずに「対戦」を表現できる
26. type User struct {
ID int64
Contact Contact
Profile Profile
Battle Battle
}
type Contact struct {
TEL, Email string
}
type Profile struct {
Image, Movie string
}
type Battle struct {
HP, ATK, DEF int
}
振る舞いを考慮すると User struct は以下になる
31. Datastore は struct をそのまま保存できるので、
データ設計の段階で struct を考慮して設計すると
手戻りが少なく、
自分でインピーダンスミスマッチを解消する必要もない
極端に言うと
Datastore設計 = モデル設計
ただし、
Datastore に保存できないデータ構造もあるので注意
32. Kind のプロパティ名に Prefix がある場合は
その値は関連性の高い値である可能性が高い
関連性の高い値は
それ独自の振る舞いや特性を持つ可能性が高い
そういった関連性の高いデータに対して
仕様を表現するロジックを紐付けることによって、
責務が明確になる