ダックタイピング
アヒルのように歩き、アヒルのようになく動物がいたら、それはアヒルだ
いかなる特定のclassとも結びつかないpublic interface
Go, Rubyとか
どういうもののことを指してダックタイピングと呼んでいるのか
こういう状況を想定している
あるclass Aの1つのmethod mに着目している
そのmethod mはObject Oを引数に取る
そのmethod m内ではそのObject OにメッセージMを送る
こういう状況
code:hoge
class A
def m(O)
O.M()
このとき、method mは、メッセージMの名前を知りうるが、
それでいても且つ特定のObject Oの存在を想定してはならない
みたいな話をしている
要は、ここでの「mのinterface」のことをダックタイプと呼んでいる 「mは、引数は「Mを持つObject」です」という暗黙の型のことをそう呼んでいる
自然言語で書くとクソわかりにくいmrsekut.icon
具体例
ダックタイピングの問題点と感じる点
状況としては、1-1.rbと3.rbは殆ど変わっていない
引数に想定するmethodの名前が変わっただけである
極論を言えば、TripCoordinator.buy_foodとDriver.gas_upを、prepare_bicyclesに名前を変更すれば意味的には全く同じ解決になる
ただ、命名と実装がズレるのが問題、という話である
つまり、methodの命名の相応しさの問題でしか無い 常に複数の選択肢を想定しながらいかに抽象的で、かつ適切な命名を発想して与えられるかという話をしている
型がないとそういう話になってしまう
「Trip.prepare_tripの責務」考えた際に、prepare_tripというmethodを持つObjectを引数に取ってゴニョゴニョする、という責務を先に確定させた上でTripCoordinatorやDriverも取れるなら引数に取る、という見方をする
その後、仮にTripCoordinator.prepare_tripの名前が変わったとしても、Trip目線では知ったことではない
それはTripCoordinatorがTrip.prepare_tripの想定からズレたというただの事実であり、もはやバグですら無い
Trip.prepare_tripが「俺が正しい。俺に遵守しないのであれば無関心」と主張する感じ
しかし、そう考えると、1-1.rbでも特に問題なくなってしまう
Trip.prepareの想定は「prepare_bicyclesを持っていること」なので。
また、複数classが必要になってから問題に気づき、命名がふさわしくないと思ってからprepare_tripにしても、少し遅い
その変更によって、Mechanicは影響を食らってしまう
prepare_bicyclesとは別にprepare_tripを定義するか、
prepare_bicyclesの名前を変更してparepare_tripに変えて、元々prepare_bicyclesを使っていた箇所を全て直すか
まあ名前の変更だけならそこまで問題はないかもだが
だから、判断が難しすぎる
1-1.rbの状況ではそこまで問題ではなく、2.rbになると大問題になる
だから1-1.rbの時点で、prepare_tripにしとけ、とこの例は主張をしている
これについは、この記事の以下の文章を読んで、あーなるほど、という気持ちになった 動的型付けオブジェクト指向言語でのプログラミングでは、引数にどのようなオブジェクトが渡されようがそれなりに動くコードを書くことが理想である
動的型付けの問題点とも思える点
この例のprepareの引数が「prepare_trip()を持つObject」だとどうやって判断するのか?
prepareの実装を読めば、あー、prepare_trip呼んでるねって気づけるけど、毎回実装内部を読んでから判断しているのか?
interfaceが云々という話をしているのに内部読んでりゃ身も蓋もなくない?とも思う
ダックタイプって意図がめっちゃ伝わりづらそう
だから文書化、テストが必要、とのこと
プロダクト全体を俯瞰した上で、「俺が今から実装するmethodの名前はこれぐらいの抽象度が適切だな」という感じで定義して、それが他の既存の同名のmethodと同じ意味合いでないといけない
例えば、今から定義しようとしているA.clacHogeは、どこか別の場所に存在するB.calcHogeとだいたい同じ意味合いのものでないといけない
使用される文脈も、抽象度も。
不可能だろmrsekut.icon
型の構造だけを見て判断するという点では似ている
公称的部分型と比較して。
Goのダックタイピング
Goの構造的部分型的な性質がなぜダックタイピングと呼ばれるのか
code:go
type Duck interface {
cry()
}
type Cat struct {
}
func (c Cat) cry() {
fmt.Println("Cat")
}
func cry(obj Duck) {
obj.cry()
}
func main() {
cry(Cat{})
}
思ったこと2019/10/22mrsekut.icon
動的型付けの言語でのダックタイピングはまだわかりやすいよ
そのクラスが関数内で必要なメソッドを持ってれば良いんだろ
わかりにくのはGoだ
型がないことはプラスに働くのか?
いまのところ全くそうは思えないけどmrsekut.icon
参考
この本はかなり動的型付け寄りの話をしている