概念としてのインターフェイス
例のお祭り騒ぎですが、ここ 2週間ばっかり、私は御輿の上で踊っているだけで、何の有意義なことも書いていないな、と思いました。やっぱり blog のおいしさってこういう集合知だなぁとしみじみありがたみを享受していたのですが(ごちそうさまです)、貰うばかりではいけないなぁ、と思います。
とは言え、私はたいしたものをもっていないので、単なる雑文になってしまうのですが。
* * *
GoF の Design Patterns は 1995年に発行されました。もう 12年も前なんですね。この本は C++ と Smalltalk でコードがかかれていて、また UML じゃなくて OMT法とか使ってます。そこだけ古さを感じさせますね。この時期に私はプログラマではなかったので、ホントのところは解りませんが、当時は Smalltalk でプロトタイピングして C++ で実装というのが ナウかったらしいですので、このチョイスはそういうことなのかしら。(おっと、初っぱなから脱線^^;)
Smalltalk はよく「デザインパターンの宝庫」と呼ばれます。事実、GoF のデザインパターンは Smalltalk でよく見かけるものばかり。悪い言い方するとパクリです。もちろん頻出パターン集だからそれで良いのだ!でも、デザインパターンの中には 完全に抽象なクラス - interface がしっかり書いてある。これってどういう事?
* * *
例えば Observer パターンを見てみます。このパターンはあるデータに対して複数のビューとコントローラを取り付ける場合、誰もが一度は落ちるスパゲティコードに対する解決策です。
(図は別の研修用につくったものなので、Bitなんちゃらとか何?とは突っ込まないでくれるとありがたいです^^; BitArray に保持されている Bit が更新されると BitArray から読み出される値も変化する、という前提で読んでいただけると幸いです)
このデザインにたどり着くまでをちょっとなぞってみます。普通に機能毎にモジュール分割してして作ると
のようになって、反対側のコントローラでデータを更新すると、こっち側のビューは古い値(嘘の値)を表示したままになります。このバグを修正する方法として、まず最初に考えるのが、BitArrayFrame と BitButton のそれぞれのコントローラが相手側のビューにも「表示を更新せよ」と通知することです。
しかし、コントローラがいちいち全てのビューに「表示を更新せよ」とかやると、コードがスパゲティになります。一つビューを増やすだけで大騒ぎです。なのでデータクラスが「誰から更新されたか知らないけれど自分が更新されたらそれを全てのビューに通知する」とした方が、似たコードがそれぞれのコントローラに分散しないのでシンプルになります。
しかし、表示をするビューを増やす度に データクラスに手を入れるのはナンセンスです。そのため「表示対象登録機構」をつくって、事前登録したビューに「更新したよ」と通知するようにします。(その為には通知される側を同じように扱えないと困ります => これは ポリモーフィズム を使った設計です)
しかし、この「表示対象登録機構」はいろんなところ(データクラスの数だけ)で同じコードが発生します。コードがコピペになってるので、これは括りだして共通化しましょう。(つまり 差分プログラミング です。)
こんな風に設計をオープンクローズの原則に従って変形していく内に、いつの間にか Observerパターンが出現しているという。デザインパターンの神秘です。(えっ、わざとらしい? ^^;)
* * *
Observerパターンを動的言語で素直にコーディングする場合、Observer クラス を作る必要はありません*1。↑の図のまるになっている部分は、動的言語で実装した場合、これを示すコードはありません(必要ありません)。でもここに(明示的に定義したりしたりはしないけれど)「なにか」が居るのは明らかです。
この「なにか」を インターフェイスと呼びます。インターフェイスという概念は OOP でポリモーフィズムを使った設計をする際には、静的型付けだろうが、動的型付けだろうが、自然に出現するものです。GoF の デザインパターンでも そういう意味で インターフェイスという言葉を使っている箇所が散見されます。
注目したいのは、この Observer パターンの構造は、綺麗なシンメトリ的な図に見えながら、Subject 側の継承と Observer 側の継承では、意味が全く違うと言うことです。C++ のような class だけしかない言語では気がつきにくいのですが、動的言語の場合そもそも Observer が存在しないので、そして Java風言語の場合は Observer が class ではなく interface になるため、この大事な違いにプログラマが気がつきやすいということ。これが騒動の発端となった私のエントリで言いたかったことだったり。(3度目の正直)
* * *
ちょっと話を戻して。インターフェイスは、動的言語であっても静的言語であっても そこに確かに何かがあるのは確かです。しかし動的言語の場合には、それをプログラマが自ら書く必要がありません。一方、静的言語の場合だと、コンパイルを通す為にこれに実体を与えなければなりません。型として記述して、それを継承しなければ、コンパイルエラーになってしまいます。
しかし、動的言語であっても「そこに何かがある」を明示して置いた方が可読性に優れる場合があるでしょう(無い場合のほうが読みやすい場合もあるけれど)。
Objective-C の protocol は、多分そうした機能なのかな?と思います。複数のメソッドシグネチャを一つのモノに束ねて名前をつけることによる可読性向上(Smalltalk の プロトコルみたいなものかな)、@interface 部に 一々おなじメソッドシグネチャを書かないですむ省力化やコードがスッキリする効果、id型を拡張する型宣言とprotocolへの適合チェック と言う プログラマを支援してくれる嬉しいオマケ機能。
Objective-C の protocol をこの視点から見た場合、Java の interface とは確かに使い勝手が大分違うモノで、まぁ、動的大好きな猫的にはこっちのほうが好きかなぁ、とか思います。(静的型チェックエラーをワーニングに留めてくれることで、ダック・タイピングのパワーをスポイルしないあたりが格好いいです)
・・・あっているかな? 私は Objective-C を本当にちょっと囓った程度なので、こんな解ったつもりなことを書くのは、怖いモノ知らずなのだけれど。
* * *
最後に、動的言語 / 静的言語 と書いてきましたが、この本質は 動的か静的かではなくって、structural subtyping か nominal subtyping か 、という話になるそうです。(静的言語でもダックタイピングが可能ということ)