6-4-3: コンポーネントの内部の仕組み

コンポーネントは、その内部のについて何も知らなくても利用することができます。 しかし、その設計を理解しておくことは、 アプリケーションを開発する上でもきっとプラスになるでしょう。

・ ここでも歴史的な視点が必要です。 古いタイプのコンポーネントの実現の仕組みから調べてみましょう。 Javaの AWT(Abstract Window Toolkit)は、マシンやシステムに依存せずに グラフィック出力やデバイスからの入力をコントロールする仕組みを実現しています。 java.awtのコンポーネントについて、改めてそれを示したのが下図です。

Buttonクラスの内部には ButtonPeer というインターフェイス を実装したクラスのオブジェクトが含まれます。 ButtonPeerを実装したクラスは、システムごとに名前が異なります。 このクラスが内部でそれぞれのシステムに依存するコードを呼び出す仕組みです。 Buttonのオブジェクトの実体を作り出すのも、この内部に隠されたクラスの仕事です。 それぞれのウィンドウシステムに用意されている ボタンに相当するのオブジェクトを生成します。 たとえば Solarisであれば Xウィンドウの Motifの XmPushButtonウィジェットが 生成されます。 ただし、通常の AWTのレベルでは内部で何が行われているかは見えません。
上のような方法は JDKの側の記述が著しく簡略化できるという利点があります。 しかし、その一方でシステム側の既存の機能に依存するため、 Javaのレベルで自由なコントロールができません。 また、コンポーネントのデザインがシステムごとに異なってしまうという問題点も 生じます。

・ Swingコンポーネントは描画部分を100% Javaで記述しています。 (もちろん Graphics や Image が抽象クラスである点は変わりないので、 「三層構造」によってシステム依存を覆い隠している点は同じです。) システム固有のウィンドウオブジェクトに頼らないため、 デザインは自由にコントロール可能です。 また結果として、ウィンドウ資源を浪費しないいわゆる "Light Weight"オブジェクト を生成することになるため、一般に実行効率も向上します。
ただし実装を持つ下部のクラスの層と、 その実装には依存しないで機能のみを抽象的に取り出した上部の層に分離するという 考え方は、やはり存在します。 たとえば JButton は Button の ButtonPeer に似た存在として ButtonUI というデータを持っています。 ButtonUI は javax.swing.plaf というサブパッケージに含まれる抽象クラスです。 ちなみに plafは "pluggable look and feel" の略で、 「取り外して交換可能なコンポーネントのデザインの方針」というような意味です。 このサブパッケージには他のコンポーネントで同様な働きをする抽象クラスが 集められています。
JButton自身は自分がどのようなデザインに描かれるかは知りません。 それを決定するのは ButtonUI のオブジェクトの役目です。 ButtonUI が実際に何という名前のクラスのオブジェクトとして実現されるかは、 状況に応じて変化します。 プログラマが特別な実装を行わない場合には、 javax.swing.plaf の中にある javax.swing.plaf.metalサブパッケージに含まれるクラスが利用されます。 しかし、それ以外の実装のクラスに切り換えることも可能です。 それに応じて JButtonのデザインも変化することになります。 注意してほしいのは、 それを決めるのはマシンやシステムの違いではないということです。 ButtonUIの実体のクラスは アプリケーションの側で明示的に設定し直すこともできますし、 指定がない場合には 仮想マシン起動時の環境設定の情報から最適のものが選択されます。 (その管理を行うための UIManager というクラスが存在します。)
ButtonUIが主に外観の実装を行うのに対して、 ButtonModelという抽象クラス(インターフェイス)も存在します。 これは主にボタンとしての機能の実装を分離して定義するためのものです。 ボタンがどのように操作されているか、それに伴ってどんなイベントを 発生させるかを定義することができます。 特にプログラマが指定しない場合には、ButtonModelを実装した DefaultButtonModel というクラスが JButtonで用いられます。