- - PR -
インターフェイスによる継承と抽象化クラスによる継承の使い分け
投稿者 | 投稿内容 | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2007-08-29 12:39
いつもお世話になっています。
インターフェイスによる継承と抽象化クラスによる継承の使い分け について、意見を伺いたいのですが、 インターフェイスによる継承は仕様だけを継承する場合、 抽象化クラスによる継承は実装も継承したい場合に利用すると理解しています。 例えば、「あああ」と「いいい」クラスがあり、 メソッドA()とB()は内容も同じ、C()は内容が違う場合、 A()、B()メソッドのみ抽象化クラスとし、C()メソッドはインターフェイス として、「あああ」、「いいい」クラスはそれぞれを実装すればいいのでしょうか。 また、「あああ」「いいい」「ううう」という3つのクラスがあり、 「いいい」クラスのCメソッドだけ内容が違う場合、 インターフェイスを継承するのか、抽象クラスを継承して、 「いいい」クラスのCメソッドだけオーバーライドして、 内容を記述するのか、そのあたりの使い分けについて、意見をいただけないでしょうか。 | ||||||||
|
投稿日時: 2007-08-29 12:52
Cメソッドが抽象クラスの機能なら抽象クラスに。
機能である必要がなかったり、他でも使うのだったらインターフェースに。 | ||||||||
|
投稿日時: 2007-08-29 13:00
mioさん、レスありがとうございます。
質問があるのですが、
Cメソッドが抽象クラスの機能であって、継承先では処理内容が異なる場合、 抽象クラスでCメソッドでは実装せず、継承先クラスでは、Cメソッドは オーバーライドにて処理内容を記述するのですか。 | ||||||||
|
投稿日時: 2007-08-29 13:24
>インターフェイスによる継承と抽象化クラスによる継承
インターフェイスの方は実装という関係になります。 抽象クラスAと派生クラスBの関係は「BのインスタンスはAのインスタンスでもある」が成り立つことです。(よくいわれるようにis-a関係)。 一方インターフェースの実装クラスはそのインターフェースのメソッドを実装するクラスでなければなりません。「□△○できる」ことを保証するものです。基本クラスライブラリにもインターフェースの名前は…ableが多いことからも、そういう使い分けをするのがわかりやすいと思います。 派生クラスがインターフェースのメソッド実装することはサイボーグがパーツを装着して新しい能力を得るという感じです。 インターフェースの定義はその装着のためのまさに「インターフェース」(入出力部分、表面)を事前に公に決めておくということです。いったん決めておけば、それに従う新しい新しいパーツを使いたくなったら差し替えることが容易になります。 ただしインターフェースは公にしてしまうと基本的に変更してはいけませんので慎重に決めなければなりません。 | ||||||||
|
投稿日時: 2007-08-29 13:58
IIJIMASさんの補足になりますが。
私もそうでしたが、オブジェクト指向に慣れないうちは、 どうしても継承やポリモーフィズムの目的を 「コードの重複を防ぐための手段」のように考えがちです。 実際この考え方も間違いとは言い切れませんし、 ときには必要になると思います。 しかし、基本的には意味的な関係を考えて クラス設計をしていくべきですし、その方が綺麗に設計できます。 つまり、このメソッドとこのメソッドの処理が同じだから継承させるとか、 そういう機能的な捉え方で継承を考えるのではなく、 構造的な捉え方をしていったほうがよいと思います。 ですから、今回の質問に一般的な回答はないと思います。 「あああ」「いいい」「ううう」のクラスが、何を表すクラスで 「A」「B」「C」のメソッドが、何をするメソッドなのかによって 答えが変わってくると思います。 | ||||||||
|
投稿日時: 2007-08-29 14:05
IIJIMASさん、レスありがとうございます。
is-aの関係にあるのは、インターフェイスではなく、抽象化クラスの継承関係だと思うのですが。
質問させていただいた上記の例は、is-aの関係なので、継承を使うと考えられます。しかし、「あああ」クラスでも「いいい」クラスでもCメソッドの内容が違う場合は、 オーバーライドを使うと、抽象化クラス(親クラス)では、空のCメソッドを実装しなければなりません。その点が気持ち悪いというか、引っかかるのです。 | ||||||||
|
投稿日時: 2007-08-29 14:13
例について言うと、
個々のメソッドの実装の継承の都合は、一旦、置いておいて、 まず、話題A・B・Cの結び付きの強度を重視する必要があると思います。 http://msdn2.microsoft.com/ja-jp/library/ms229013(VS.80).aspx MSDN ライブラリ / .NET Framework 開発者ガイド / クラス ライブラリ開発のデザイン ガイドライン / クラスまたはインターフェイスの選択 などでは、 > インターフェイスはその後のバージョンで新しいメンバを指定できません が強調されているように感じます。 なので、インターフェースのデザインは安定させる必要があると思われます。 「使うものは何でも捻じ込む」方式では不安定になり易いと思われるので、 必要なら「関連の強い話題で分割して多重継承」も検討すべきと思われます。 また、ADO.NET 2.0のSystem.Data.Commonの設計の話などを読むと、 1)interfaceを用意する。 2)1の基本的な実装classを用意する。 3)2の各種派生classを実装する。 というパタンも考慮する必要があると思われます。 CLRでは、classの多重継承はサポートされていないため、 もし、1が無いと、逃げ道がなくなる可能性があります。 (※こっちは想像。 何故、class DbXXXが、interface IDbXXXを継承しているのか。 ADO.NET 2.0のように、引き継ぐべき過去があるならともかく、 System.AddInなど新しい話題でもinterfaceを使って設計している。) もし、2が無いと、実装の共有の余地がありません。 interfaceにメンバを追加した途端、 各派生クラスとの互換性がなくなります。 (※こっちは「だから基本クラスを用意した」と書いてあったと記憶している。) [ メッセージ編集済み 編集者: NABE 編集日時 2007-08-29 14:27 ] | ||||||||
|
投稿日時: 2007-08-29 14:18
クラス「あああ」「いいい」間で ・A(), B() の実装が共通であること ・C() の実装が異なること という事実だけからは判断できません。 できる場合もあるでしょうが、それはおそらく実装クラス「あああ」「いいい」が既に存在していて、その共通項を洗い出して共通の基底クラスを作り出そうという一種のリバースエンジニアリング的なことをしている場合の話になると思います。 本来、設計はトップダウンであるべきで ・「あああ」「いいい」クラスの基底となるクラス「ほげ」の責務は予め分かっている ・「ほげ」の責務が明らかなのであれば、「ほげ」が実装するべきメソッド(仮想メソッドも含む)も明らか であるはずで ・A(), B() の実装が共通であること ・C() の実装が異なること という事実は単なる結果に過ぎないはずです。
これも同様です。基底クラスに求められている責務を明らかにするのが先と思います。 |