- - PR -
インターフェイスによる継承と抽象化クラスによる継承の使い分け
投稿者 | 投稿内容 | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2007-09-05 11:11
ここまでを流し読みしかしていませんが…。
そもそもインターフェースを必要とするクライアントがいなければ、いくらインターフェースを実装・公開しても全く意味がないのでは。 なので、まず「最初に有りき」なのが「クライアント」ですよね? 「is-a」だとか「〜ができる」だとかが最初の動機になる事はないはずです。 あるクライアントが他のクラスを使いたいんだけど、実装の詳細が分からない(或いは知りたくない)。だけど、「〜できればとりあえず良い」という事でインターフェースだけを必要とする(ある程度実装を必要とするなら抽象クラスを必要とすれば良い)。 で、そのクライアントに使って欲しいと思うクラスを書くならインターフェースを実装する以外に道はないわけで。インターフェースを実装する動機って普通こうではないですか? クライアントもいないのにインターフェースを実装するのか抽象クラスを継承するのか悩むのは違うかなぁと思います。 _________________ 囚人のジレンマな日々 | ||||||||||||||||
|
投稿日時: 2007-09-05 11:46
すいません。素朴な疑問なんですが、ここでいうクライアントってなんですか? 具象クラスのこと?他の開発者のこと?お客さんのこと?あるいはそれ以外ですか? 文脈からだけでは何のことを「クライアント」とおっしゃっているのかよくわかりませんでしたので、教えてください。 | ||||||||||||||||
|
投稿日時: 2007-09-05 11:50
失礼しました。 「具象クラスのこと」です。具象でなくても、抽象でも他のインターフェースでも良いですが。 プログラミング言語の場合もあります。例えば C# の foreach とか。 _________________ 囚人のジレンマな日々 | ||||||||||||||||
|
投稿日時: 2007-09-05 13:53
「クライアント」の意味付けについて、ご返答ありがとうございます。
具象クラスだけでなく、サブクラスとしての抽象クラス、インタフェースも含む ということですね。
さらにいうと単にサブクラスだけを意図するわけでなく、 そのクラスを使う場面・用途といったニュアンスも含んでいるということでしょうか。 foreachのためにIEnumerableを用意したように。
つまり、「どのように使うのかを想定せずに」インターフェースを実装するのか抽象クラスを継承するのか判断できないのでは?という理解であってますか? #+アルファの情報提供がなくてすみません。ちょっと気になったもので>ALL | ||||||||||||||||
|
投稿日時: 2007-09-05 14:18
私の説明が足りなくて誤解があったかもしれません。
Client クラスは IHogetable インターフェースのクライアントです。
私が言いたいのは、サブクラスを最初に考慮してはだめで、そのインターフェースを使う場面を最初に考慮すべきという事です。
はい。その通りです。インターフェースは特にその意味が強いと思います。抽象クラスはその限りではないかなと思いますが。 _________________ 囚人のジレンマな日々 | ||||||||||||||||
|
投稿日時: 2007-09-06 01:22
十分です。おおいに参考になります。
なるほど。 将来の拡張の可能性への配慮、すなわち柔軟性を考えるとそうなっちゃいますよね。 しかし、現時点の仕様だけを見ると、あるテーブルに対して削除はないので、 「Delete()がコールされた」としたら、これはコーディングミスである可能性が高い。 IsDeletableを見るまでもなく、Delete()自体コールされる機会は無い筈。 構造上はIsDeletableを無視して、Delete()をコールすること自体は可能なわけで。 このミスをコンパイルの段階で発見できるのが良いのか、 動かしてから例外という形でわかるのが良いのか、 それともミスを吸収してしまう(コールされても何もしない)のが良いのか。 この「コーディングミスへの配慮」と「拡張の可能性への配慮」のトレードオフが この問題の肝だと思っています。 結局ケースバイケースってことになっちゃうのかもしれませんが、 DBのテーブルのラッパなんてのは結構良くでてくる話だと思うので、 皆さんどうされてるのかなぁと思った次第です 追記: >IsDeletableを見るまでもなく、Delete()自体コールされる機会は無い筈。 よく考えたら使う側の設計次第では、ありますね。 View側も継承なり委譲なりで一部共通化していた場合。 例えば、削除ボタンのイベントハンドラは実装しちゃってあって、 ボタンのVisibleをIsDeletableとひっつけとくとかできるわけだ。 | ||||||||||||||||
|
投稿日時: 2007-09-06 02:30
最後の「吸収する」のは、「実行できるがなにもしない」ということですので、 「エラーが起きても返さない」というのとは意味が異なりますよね。 「実行できるがなにもしない」というのが意味論的に正しい場合は例外を投げず、 間違っている場合には例外を投げるようにすべきだと私は思います。 私は「ミスをコンパイルの段階で発見できる」というのに あまり重きを置かないようにするべきだと考えています。 語弊がありますが、IDEの支援機能程度と思うようにしようと。 これを考えすぎると、クラス数の爆発が起こり、 何も開発できなくなる人が多々現れるからです。 (私の環境のせいかもしれませんが。) 例えば。(仮想ライブラリです。Integerも仮。) Integerを派生したNonZeroIntegerを作った人がいます。 NonZeroInteger Integer.Power( Integer value ) Double Integer.Divide( NonZeroInteger value ) となるそうです。 このクラスを使えば、/0エラーを発生させてしまう場合が減るに違いないと。 そして、次にNonNegativeInteger, NaturalNumberをつくり、 NonNegativeInteger NonNegativeInteger.Power( NonNegativeInteger value ) NonNegativeInteger Integer.Add( NonNegativeInteger value ) Integer NonNegativeInteger.Subtract( NonNegativeInteger value ) NaturalNumber NaturalNumber.Power( NaturalNumbervalue ) … と、延々と実装していき、終わらなくなりました。 ほぼ同じ間違いを私の周りではかなり見ます。 あちこちで問題になるように、クラス分けの明確な基準が存在しません。 そのため、勘の働かない人が作ると、クラスを分け続けてしまうのです。 継承が無くてもまともに動くプログラムは作れます。 継承を考えすぎて作れなくなるくらいなら、 なるべく使わないようにして、 使ったらうまく行きそうな所が見えるようになるまで経験を積むほうがいいと。 old-styleな職人のような意見で申し訳ないですが。 これに関しては習うより慣れろ。かな。 他の人の意見を聞きたいですね。 これを読めば継承で失敗しない!みたいな文章があるといいんですが。 |