継承ではなく包含でいい理由

今、あるライブラリを弄っていて
"あ、こいつの OpenFile() ってファイルっていいながら Stream を返すんだなぁ"
と思いながら、今では語りつくされた感の強い、継承よりもコンポジションを使え、という話についてふと考えた。


例えば、File は Stream を継承せずに包含する形にして何か困ることは発生しないだろうか?
可能性を考える。


●case "Stream のメソッドが呼ばれて、その中で File のメンバーを使いたい"
メソッドを File でオーバーライドしたのならば、そのメソッドは File のメンバーにアクセスできる。


●case "Stream として渡された File にコールバックしたい"
Stream のインターフェイスとして用意されたものをオーバーライドしているなら問題ないはず。
Stream のインターフェイス以外を使おうとするなら静的型付言語として不正な振る舞い。


というわけで、困ることはない。はずなんだけど、前に何かこういう状況で困ったことがあったような気がするんだよなぁ。
どういうシーンで困ったのか思い出せない。
思い出したら追記したい。



って書いて一服したら速攻で一例思いついた。
C++ のようなガベコレのない環境において、オブジェクトの破棄の責任を Stream として管理したい場合には問題だ。


この場合、Stream の定義も変更可能ならば、何か削除可能なインターフェイスへのポインタをメンバとしてもっておいて File にはそのインターフェイスを実装させ、デストラクタでそれを delete すればいい。
Stream の定義が変更不能ならば、File として Stream を継承するのではなく、"破棄責任のあるオプショナルなオブジェクトを持つストリーム" として Stream を継承し、上記の方法を使うのはどうだろう?
さらに、Stream のインターフェイスのみが独立して定義されているなら Decorator としてそれを定義すれば、別の継承ツリーの Stream を包含できる。


というわけで、さらに何か例を思いついたら追記する。