minahitoの鯉日記スマートフォン用の表示で見るminahitoの鯉日記この広告は、90日以上更新していないブログに表示しています。2009-10-17関数コールバックかインターフェイスか#xoopscubexoopscube.jpでのonokazuさんのこの書き込みはかなり的確なところをついている。XCの設計コンセプトとしては、onokazuさんが書かれているとおりです。onokazuさんがコールバック等を記述した設定リストくらいにしておいて、インターフェイス実装を避けた方がいいのでは、と書かれているのに対して、僕のほうからはインターフェイスないしは抽象クラスを使うかもしれない旨を返信しています。僕の主張は、たとえインターフェイス実装スタイルをとったとしても、それは書き方の問題でしかなく、モジュールとインターフェイス提供パッケージが"依存性の強いズブズブの関係"になってしまうことを意味しないというものです。確かにコールバックにすればその"見た目の関係"はもっとすっきりしたものになる。仮想デリゲートが用意されている。関わらずインターフェイスクラスを作るかもしれないと書いたのはパフォーマンス上の理由によるものだ。面白い話なので、少し補足する。C言語やC#のように「関数アドレス」をハンドリングすることができる言語では、モジュール間の接点を関数コールバックで行うことで、依存性を大幅に下げることができる。関数アドレスがハンドリングできないJavaなどではインターフェイスにレスポンスするコードを実装して、それをコールバック発信主に登録するという方法が多用される。Listenerクラスが代表格と言える。関数コールバック最近見かけた実例として、組込スクリプトエンジンのリモートデバッギングまわりの構成を挙げる。スクリプトエンジンのランタイムはターゲットマシンで動作し、PC上のリモートデバッガと接続して、互いにパケットを送り合っている。スクリプトエンジンパッケージに含まれてもいなかった。これらのシステムはコールバックの接続で連携を行う。接続を行う形をとった。各モジュール間には依存性はないため、簡単に別物に取り替えることができる。「オーバーライドで拡張が容易」という表現ではなく「完全な別物と変更可能」になる。ターゲットマシンのカーネルAPIベッタリのコードに差し替えることになるだろう。変更すべきはタイトルパッケージ所属のグルーコードだけだ。スレッド動作することになるから、独立していることは有り難かった。モジュールの同等のメンバ関数へ中継するグルーコードを記述した。内のわずか二箇所に留まっている。ローレベルの接続は、とてもC言語らしい書き方だが、C#でもデリゲートの用途としてお馴染みのやり方になっている。後述するが、もうひとつのやり方である「インターフェイス実装」は、単一継承しかできない言語ではステップがややこしくなることがある。単一継承ながらデリゲートが使用できるため、ケースバイケースでそれを使い分けることができるのが利点だ。インターフェイスこれもよく使われている方法で、Objective-Cのデリゲート(正確にはプロトコル)もこちらのタイプにあたる。インターフェイスを通じた通信は必ず同一のインスタンスに送信されるため、実装が非常に楽で、接続もインスタンスアドレスの引き渡し一発で済むというメリットがある。裏を返せば、関数ポインタやC#デリゲートのように接続の組み合わせが自由ではない。よって、インターフェイスに対し行われる通信を複数のモジュールで受け持つ構成を作る場合は若干手間がかかる。最低限として各モジュール間の独立性を保つにしても、ほぼアダプタークラスが必須になり、それを更にグルーコードから切り離そうとすると、2層3層の構造になってしまう点も若干面倒と言える。先のスクリプトエンジンの例を単純にインターフェイスを通じたコールバックに変更するならこのようになるだろう。使わない限り単一継承なので、もう1層間に入ることになる。いっても、基本的にはジャンプアドレスをどういう形で引くかの違いでしかない。委ねるかどうかの違いだ。基本的にはコードを書く側にとっては「表現方法の違い」の部類に入る。いう意味では宗教戦争の誘因材かもしれない。*1先ほど書いたように、C#の場合はデリゲートとインターフェイスをケースバイケースで気軽に使い分けられるのから強力である。C++も同じだが、関数アドレスには型の問題が残りがちで、インターフェイス実装インスタンスの引き回しでは参照解除/メモリ解放タイミングがしばしばトラブルの要因となる。リファレンスのメモリ管理があるからこのへんは気にしなくていい。インターフェイスを使うかもしれないのかXCにデリゲートを突っ込んだことからも分かるように、個人的にはローレベルのフィーチャーが好きだ。アダプターを挟まなくても各モジュールの独立性を保てる点からいっても、インターフェイス実装より、関数単位のハンドリングとコールバックのほうが、XCのコンセプトに向いている。*2しかし、パフォーマンスを考えると、PHPではインターフェイスなり抽象クラスなりを使わざるをえないのではないか。PHPではJava同様、関数がハンドリングできない。そこで文字列をパラメータにして、専用のコール関数を使ってコールバックを行う。文字列の時点では関数のシンボルは解決できていない。関数のグローバルシンボルテーブル内を、文字列を使ってハッシュ検索して、都度シンボル解決してることになる。その後関数オブジェクトを掴めるわけでもないので、コールのたびにこの負荷がかかる。一言で関数コールバックといっても、シンボル解決済みの素のアドレス情報をそのまま使って即ジャンプするC言語と、文字列からシンボルテーブルを都度検索するPHPでは、実行負荷は大きく違うものになる。このエントリでは関数ポインタとインターフェイスを比較したが、関数ポインタなんてものはない。なると、判断は変わってくる。インターフェイスや抽象クラスにしておけば、それを実装したサブクラスのインスタンスのテーブルのメンバを検索すれば済むことになるから、ずっと軽いのではないか。コールバックの種類(機会)が増えれば増えるほど、パフォーマンス的にはインターフェイスのやり方のほうが有利になる。この負荷の違いが、種類×量に対してかかると考えると、どうせ同じことができるならインターフェイスのほうが良いのではないかと思えてしまう。それがインターフェイス実装スタイルを採用することになってしまうかもしれないと書いた理由だ。繰り返しになるが、アダプターを書くだけの話なので、これが独立性の高いプログラムモジュールを外部から突き壊す要因にはならない。エンティティモデル(2)可変実装を考える前に、不変実装を振り返ってみます。»プロフィールid:minahito_carp今年こそ広島が優勝したブログ。(1)はてなブログをはじめよう!minahito_carpさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。