- - PR -
あるクラスの paint メソッド内でサブクラスの paint メソッドを呼びたい
| 投稿者 | 投稿内容 | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2003-07-06 11:37
unibon です。こんにちわ。
みなさまありがとうございます。
これは私の2つのサンプルの内の前者の方の、 paint2 メソッドを使うやり方と等価だと思います。 ちょっと、最初に目的を明確に書かなかったのですが、 私は paint メソッドというメソッドは、サブクラスで挙動を再定義できるために コールバックされるメソッドという位置付けであると理解しているのですが、 そのためのコールバックメソッドが paint と別に paint2/paintsub ができてしまい、 インターフェースが「汚く」なってしまうのを避けたいという思いがあります。 「汚い」というのは、なんと言うんでしょうか、 もともとコールバックの定義のために paint というメソッドがあるのに、 同じ目的のために別のメソッド(paint2/paintsub)を定義しなければいけないのは、 なんだか嫌だ、と言う漠然とした感じです。
この実行結果は、
と、なりますが、 "!" は PrintSub1 クラスが出力し、 "?" は PrintSub2 クラスが出力しているので、 それぞれ「別のサブクラス内で処理されている」と言えるはずです。 #もしかしたら言葉の解釈のほうに相違があるのかもしれません。 | ||||||||||||||||
|
投稿日時: 2003-07-06 11:56
unibon です。こんにちわ。
たしかに、無限ループ(再帰が無限)のようなことが起こりうるようだし、 呼ぼうとしたサブクラスのメソッドが未定義だった場合の対処などが必要となりそうで、 複雑でなんだか良く分からなくなってきました。 ただ、できるにしてもできないにしても、 なにか理論のよりどころはぜひ知りたいと思います。 以下はちょっと冗談っぽくなりますが、以前に見つけた興味深いものとして、 http://homepage3.nifty.com/iromono/hardsf/ctccomp.html で紹介されているような突飛な理論に、この話題は近いのかなあ、 などと思えてきました。 #そこまでは突飛ではないのですが(笑)。 | ||||||||||||||||
|
投稿日時: 2003-07-06 12:50
unibon です。こんにちわ。
コンポジションだと、メソッドの名前は paint ひとつにまとまりますが、 逆に、インスタンスが2つ必要になると思います。 たとえば、java.io.FilterInputStream などもコンポジションを使った典型例であり、 一般的なソリューションですが、2つのインスタンスが要るのが、 私の好みも混じってしまいますが、なんとなく敬遠してしまいます。 でも、教えていただいた ExpansionCanvas のサンプルなどを拝見すると、 やはり実用的なのはこのやりかたなのかな、と感じます。
そうです。コールバックをいかにキレイに定義するかで悩んでいます。 ただ、思ったのは、Java のメソッドのオーバライドは、 コールバックに使えますが、それ以外に、機能の拡張(文字通り extends)という用途にも使え、 本来は、メソッドオーバライドには意味合いが2つ(やそれより多く)あるのかもしれません。 このあたりを整理すると良いのかな、とも思うのですが、良く分かっていません。 | ||||||||||||||||
|
投稿日時: 2003-07-06 16:11
unibonさん、こんにちは。
Java1.5で導入予定のGenericsで、きれいに実現できると思います。 コンパイルしてみた訳ではないので、間違っていたらごめんなさい。 継承関係は逆になっています。
[ メッセージ編集済み 編集者: ocean 編集日時 2003-07-06 16:13 ] | ||||||||||||||||
|
投稿日時: 2003-07-06 22:28
unibonさん、こんにちは。
説明が言葉足らずでした。失礼しました。 ”同じサブクラス内で処理”とは、 別のクラスのメソッド Print.printer() が PrintSub1.printer() を呼ぶのではなく、 同じクラスのメソッド PrintSub1.printer() が PrintSub1.printer() を呼んでいた、 と言ったつもりでした。
スーパークラスに固有処理を書いてしまったら汎化−特化関係が 成立しないのではないでしょうか。 共通部分を括るのが汎化です。sub.paint()は共通でない処理だから サブクラスに書くのだと思います。共通処理が共通でない処理に依存 したら、”共通”ではなくなる気がしますが、どうでしょう? | ||||||||||||||||
|
投稿日時: 2003-07-07 11:52
田村です。こんにちわ
たとえ、インターフェースが増えても、再利用性、拡張性が向上するのであれば 1つのメソッド(継承関係にあるものも含む)で解決するのではなく、 複数のメソッドに分割する方法を選ぶというのが、私の考えです。 私の提示した方法は、継承ベースのパターンであるので、 クラス数の増大を避けることはできません。 この場合、描画に特化したインターフェースを作り、 paint() メソッド内でこのインターフェースのメソッドに処理を転送する方が むしろ処理がすっきりするのではないかと思います。 呼び出し元のプロパティが必要な場合は、描画クラスに必要な値を渡したのでは インターフェースが複雑化する場合、描画クラスに処理を委譲するのもありだと思います。 | ||||||||||||||||
|
投稿日時: 2003-07-07 19:12
同感です。ただ、既存の Canvas に拡大機能をつけたいときは、テンプレート+継承も使える方法だと思います。 ///////////////////////////////////////// sub.paint()みたいな書き方は、理論的には可能だと思います。(ただ、仮想関数の仕組みについて詳しいわけではないので、とんちんかんなことを言っているかもしれません。
仮想関数のポインタをvtableに格納する際、通常と同じくまず、Derived##hoge() のポインタを格納しようとします。しかし、watch_parent_on_vtable_creation が定義されているので、代わりに Base##hoge() のポインタを格納します。sub.paint()は、sub が定義されていないときはコンパイルエラーにし、無限ループについては func1() { func2(); } func2() { func1(); } をコンパイルエラーにしないのと同様、エラーにはしません。 こうすると可能な気はしますが、仮想関数の流れがあちこちに飛ぶため、処理が読めなくなる危険性があります。また、sub.paint() がどの型の派生クラスの paint() を呼ぶかを、関数ポインタで記憶するか(必要なメモリが跳ね上がる気がする)、クラスの継承樹を実行時にたどる(必要な時間が跳ね上がる)必要があります。単に関数名で置換するのは、コードの肥大化を招くと思います。
同感ですが、同名仮想関数に解決方法を求めてしまうと、他の汚さを招くため、望ましくないのでしょう。 | ||||||||||||||||
|
投稿日時: 2003-07-08 09:23
unibon です。こんにちわ。
ありがとうございます。Generics でできちゃうんですか。 Java の新しい機能はほとんど知らないので、 失礼ながらちょっと半信半疑なのですが(すみません)、 示されたサンプルと共に調べてみます。 ありがとうございます。
すみません。Java の実行時の型はひとつだけですから、前者はありえないので、 ないものだとして除外してしまった前提で書いていました。
そうですよね。教えていただいて、考えもすっきりしました。 Component クラスは paint メソッドをオーバライドする形式になっていますが、 KeyListener などのように実行時にコールバックを追加できるようにすればいいんですよね。 すなわち、おっしゃるような「描画に特化したインターフェース」として
があって、Component クラスは paint メソッドを持たず、代わりに、 public void addPainter(Painter painter) などのようなインターフェースを備えておけば、 別段、継承関係を使わずともできますね。 これは結局は「委譲」のやりかたになると思います。 | ||||||||||||||||
