traitのmixinっぽいやつ
っぽいやつじゃなく、mixinのイメージ通りのものは実装されてる方がいらっしゃるみたい。
でもgolangはエイリアス型を宣言できるので、レシーバーでメソッドを付け加えることでtraitっぽいことができるし、作ったオブジェクトの型変換だけでやりたいことは事足りるんじゃ?
構造体のフィールドはそのままで振る舞いだけを特化するので、オブジェクト指向のダウンキャストと違って安全に(アプリ動作的に安全とかではなく、ポインタは変わらないだけだけど)新しい型として振る舞えそう。
以下、妄想で書いてるのでなんの検証もしてません。
例:DCIっぽい実装
シンプルな手続き型言語であることを活かしてDCIっぽく実装できそうとか思ったので、脊髄反射的なサンプル書いてみた。
例1
package main
import (
"fmt"
)
func main() {
// 普通のたろー君
taro := NewPerson("Taro")
fmt.Printf("ポインタ %pの%sとして生まれた\n", taro, taro.Name)
// たろー君設計する
ctxDesign(taro)
// たろー君プログラマーになる
ctxProgramming(taro)
// なんならgoも書くよ
ctxGoProgramming(taro)
fmt.Printf("たろー君の人生は終わらない。ポインタはずっと同じ。%p\n", taro)
// 逝く。
taro = nil
fmt.Printf("臨終。%v\n", taro)
}
// 人だよ
type Person struct {
Name string
}
// 出産
func NewPerson(name string) *Person {
return &Person{
Name: name,
}
}
// 設計書書くよ
type DocumentWritable interface {
WriteDocument(spec string)
}
// コード書けるよ
type CodeWritable interface {
WriteCode(code string)
}
// アーキテクト
type Architect Person
func (a Architect) WriteDocument(spec string) {
fmt.Println(spec)
}
// プログラマー
type Programmer Person
func (p *Programmer) WriteCode(code string) {
fmt.Println(code)
}
// goプログラマー
type Gopher Programmer
func (p *Gopher) WriteCode(code string) {
fmt.Printf("fmt.Println(\"%s\")\n", code)
}
// コンテキスト: 人雇って設計書を書かせる
func ctxDesign(p *Person) {
a := (*Architect)(p)
fmt.Printf("ポインタ %pのアーキテクト%s\n", a, a.Name)
a.WriteDocument("こんなあぷりつくる")
}
// コンテキスト: 人雇ってプログラマーとしてコード書かせる
func ctxProgramming(p *Person) {
pgr := (*Programmer)(p)
fmt.Printf("ポインタ %pのプログラマ%s\n", pgr, pgr.Name)
codingTask(pgr)
}
// コンテキスト: 人雇ってgoプログラマーとしてコード書かせる
func ctxGoProgramming(p *Person) {
pgr := (*Gopher)(p)
fmt.Printf("ポインタ %pのgoプログラマ%s\n", pgr, pgr.Name)
codingTask(pgr)
}
func codingTask(coder CodeWritable) {
coder.WriteCode("HelloWorld!")
}
アンチパターン
オブジェクト指向言語だとエイリアス型の宣言自体がアンチパターンとして挙げられるみたいだし、モデルの振る舞いがデータと切り離されるという意味では、ドメイン駆動に対して典型的な貧血症モデル。構造体はドメイン内でどう振る舞うかなんて知らないから、パッケージ外に複雑な操作は公開しないほうがよさそう。