継承を使うべき場面
継承とは、既存のクラス(親クラス)を元にして
新しいクラス(子クラス)を定義することです。
子クラス側では、親クラスの機能を一部上書きしたり
新しい機能を追加したりすることが出来ます。
継承は強力ですが、使い方を間違えると
簡単に変な設計をすることが出来てしまいますので
まず、継承を使うべき場面について考えてみます。
継承を通して、子クラスは親クラスの機能を全て引き継ぎます。
つまり、子クラスは親クラスの一種と言える訳です。
ここがポイントで、
「
子クラスは親クラスである」
といえる関係が成り立つ場合にのみ継承を使うべきだと
一般的には言われています。
例えば車を考えると
「ワゴン車は車である」
「乗用車は車である」
ということは出来るので、
車クラスを継承してワゴン車クラスや乗用車クラスを作るのは
問題ありません。
しかし、
「タイヤは車である」
というのは若干無理があり
タイヤクラスを継承して車クラスを作るのは良くありません。
この「〜は〜である」という関係のことを
オブジェクト指向の用語では「
is-a関係」と呼びます。
継承の原則を一言で述べるとすれば
○「is-a関係」がある場合にのみ継承させるべき
となります。
また「〜は〜を持つ」という関係のことを
「
has-a関係」と呼びます。
上の例では「車はタイヤを持つ」と言えるため
車とタイヤは「has-a関係」にあります。
この「has-a関係」が成り立つような場合には
継承ではなく、集約(aggregation)または
複合(composition)と呼ばれるテクニックを用いると
うまくクラスをデザインすることが出来ます。
これについては、また後ほど説明します
継承
F#の継承はJavaやC#等の言語と同じく
単一の親クラスからの継承(とインタフェース継承)のみをサポートしています。
これはおそらく、複数の親クラスからの継承(多重継承)には
ダイヤモンド型の継承など様々な問題があるためだと思われます。
クラスの継承は、classキーワードに続けて
inheritキーワード、親クラスと指定します。
次の例ではねずみのクラスMouseとそれを親クラスに持つ
ポケットをもつねずみクラスであるPocketMouseを定義しています。
名前は外部に公開しない方がいいと考えて、memberにせずlet束縛で定義しています。
以前にやったようにlet束縛を用いた場合、クラスにprivateなフィールドになります。
継承の例
type Mouse(n:string) = class
let name = n
do
printfn "Mouse"
member this.print() = printfn "%s" name
end;;
type PocketMouse(n:string,p:string) = class
inherit Mouse(n)
do
printfn "Pocket Mouse"
let contents = p
end;;
let hatuka = Mouse("ハツカネズミ") in
hatuka.print();;
let pikachu = PocketMouse("ピカチュー","でんき袋") in
pikachu.print();;
let doraemon = PocketMouse("ドラえもん","四次元ポケット") in
doraemon.print();;
継承の例(軽量構文)
type Mouse(n:string) =
let name = n
do
printfn "Mouse"
member this.print() = printfn "%s" name
type PocketMouse(n:string,p:string) =
inherit Mouse(n)
do
printfn "Pocket Mouse"
let contents = p
let hatuka = Mouse("ハツカネズミ")
hatuka.print()
let pikachu = PocketMouse("ピカチュー","でんき袋")
pikachu.print()
let doraemon = PocketMouse("ドラえもん","四次元ポケット")
doraemon.print()
この例ではMouseクラスを継承してPocketMouseクラスを作成しています。
PocketMouseクラスは、Mouseクラスのメンバやフィールドを引き継ぐため
nameフィールドやprintメソッドも持ちます。
ドラえもんは猫型ロボットですがここでは雑にネズミの一種として扱っています。
あまりつっこまないでください。
継承を行っているのはinherit Mouse(n)の個所で継承時にあれば引数を指定する必要があります。
これにより、Mouseクラスを継承すると宣言しています。
クラスを作る際には、親クラスと子クラスの
コンストラクタの呼び出し順序を知っておくことは有用です。
上のプログラムの実行結果は次のようになります。
実行結果
Mouse
ハツカネズミ
Mouse
Pocket Mouse
ピカチュー
Mouse
Pocket Mouse
ドラえもん
これより、まず親クラスのdo-bindingが呼ばれてから、子クラスのdo-bindingが呼ばれていることがわかります。