ダックタイピングという単語を聞いたことがあるものの、アヒル以外何も頭に思い浮かばないという方向けの記事です。ダックタイピングについてGoでの実装例と共に初心者の方でも分かるようにやさしく書いていきます。
以前書いたポリモーフィズムの記事の焼き直し感が否めませんが、まあ前回の記事と今回の記事を読めばダックタイピングとポリモーフィズム両方理解できるってことで気にせず書いていきます。
##Wikipediaで理解するダックタイピング
まずはWikipediaのダックタイピングの説明を引用します。この説明を読んでよく分からなくても、記事を読み終わった後に再び読んでみると腑に落ちるはずですので、まずはさらっと読んでみてください。
ダック・タイピング(duck typing)とは、Smalltalk、Perl、Python、Rubyなどのいくつかの動的型付けオブジェクト指向プログラミング言語に特徴的な型付けの作法のことである。それらの言語ではオブジェクト(変数の値)に何ができるかはオブジェクトそのものが決定する。これによりポリモーフィズム(多態性)を実現することができる。つまり、静的型付け言語であるJavaやC#の概念で例えると、オブジェクトがあるインタフェースのすべてのメソッドを持っているならば、たとえそのクラスがそのインタフェースを宣言的に実装していなくとも、オブジェクトはそのインタフェースを実行時に実装しているとみなせる、ということである。それはまた、同じインタフェースを実装するオブジェクト同士が、それぞれがどのような継承階層を持っているのかということと無関係に、相互に交換可能であるという意味でもある。
"If it walks like a duck and quacks like a duck, it must be a duck"
(もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルに違いない)
この説明でダックタイピングがなんであるかを理解できなくても気にしないでください。実装例を見た方がわかりやすいので、これからGoでの実装例を見ていきます。
##ダックタイピングの実装
type Income interface {
calculate() string
}
type google struct {
workingYear, baseSalary, performance int
}
type yahoo struct {
performance, workingYear, baseSalary int
}
(g google) calculate() int {
return g.baseSalary + (1000 * g.performance)
}
func (y yahoo) calculate() int {
return y.baseSalary + (20000 * y.workingYear)
}
上記では、calculateメソッドを持つIncomeインターフェースを定義し、YahooとGoogleという二つの構造体型にcalculateメソッドを定義しました。
すると、以下のようなことができるようになります。
func main() {
hanako := google{
workingYear: 5,
baseSalary: 100000,
performance: 190,
}
choko := yahoo{
baseSalary: 60000,
workingYear: 50,
}
motoko := yahoo{
baseSalary: 40000,
workingYear: 25,
}
workers := []Income{hanako, choko, motoko} //yahoo, google型がincomeインターフェースを満たしているから渡せる
fmt.Printf("Total income: %d\n", calculateIncome(workers))
}
func calculateIncome(ic []Income) int {
sum := 0
for _, worker := range ic {
sum += worker.calculate()
}
return sum
}
google型もyahoo型も共にcalculateメソッドを実装しているため、Incomeインターフェースを満たしているということになります。すると、このように違う型の構造体(ここではyahoo型、google型)も、同じIncome型として扱うことが可能になるのです。
##最後にもう一度Wikipediaで理解するダックタイピング
ここで、先ほどのWikipediaの説明の一部をもう一度読んでみましょう。
オブジェクトがあるインタフェースのすべてのメソッドを持っているならば、たとえそのクラスがそのインタフェースを宣言的に実装していなくとも、オブジェクトはそのインタフェースを実行時に実装しているとみなせる、ということである。それはまた、同じインタフェースを実装するオブジェクト同士が、それぞれがどのような継承階層を持っているのかということと無関係に、相互に交換可能であるという意味でもある。
これを先の実装例と照らして読み替えてみると理解が深まると思います。
###Wikipediaを実装例で読み替え
オブジェクトがあるインタフェースのすべてのメソッドを持っているならば、
読み替え:構造体(yahoo型、google型)がIncomeインターフェースの全てのメソッド(ここではcalculate()
だけ)を持っているならば *1
たとえそのクラスがそのインタフェースを宣言的に実装していなくとも、
読み替え:GoにはJavaなどの言語と違ってインターフェースの宣言的実装はなく、インターフェースが持つメソッドを全て実装した時点でインターフェースを実装したとみなす。
オブジェクトはそのインタフェースを実行時に実装しているとみなせる、ということである。
読み替え:yahoo型、google型は両方ともIncomeインターフェースを実装しているとみなせる。(だからIncome型のスライスの要素にhanako, choko, motokoを入れられた)
もし、それがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルに違いない
読み替え:もしyahoo型、google型が計算ができるのなら、それはincomeに違いない
最後の読みかえはかなり微妙でしたが、ダックタイピングについて理解できたのではないかと思います。Goでは明示的なインターフェースの宣言がないので、インターフェースを扱おうとすると勝手にダックタイピングになりますがダックタイピングとは何なのかしっかりと理解しておくことはいずれ役に立つでしょう。
本記事で引用したWikipeiaの説明には、
それらの言語ではオブジェクト(変数の値)に何ができるかはオブジェクトそのものが決定する。これによりポリモーフィズム(多態性)を実現することができる。
とあります。ポリモーフィズムについての説明記事も以前に書いてありますので、Goでポリモーフィズムを理解したいという方はそちらもお読みください。今回の記事と説明がほとんど同じですので、すんなりと理解できると思います。Go言語でポリモーフィズムとその便利さを理解する
それでは、最後までお読みいただきありがとうございました。
###注釈
*Goにはオブジェクトはありませんが、構造体を代わりに使って似たような振る舞いを実現できます。