戻る

ウィジェット

   本章では、多態性を使った具体例として仮想的なGUI(グラフィカルユーザーインターフェース)を実現するプログラムを取り上げる。
  GUIの部品(ウィジェットという)として、チェックボタンやラジオボタンを作り、それをウィンドウの上に配置する。
   ただし、ここで作成するプログラムは、あくまでも多態性を説明するのが目的なので、それほど凝った事は行わない。
  チェックボタンと言ってもグラフィカルなボタンではなく、ただラベルとボタンのチェック状態を表示するだけの仮想的なものである。
  その他のウィジェットについても同様である。

ウィジェットの祖先

   ウィジェットというのは、グラフィックスを使ったユーザーインターフェースでよく使われる部品
  (例えば、ボタン・メニュー・スクロールバーなど)の事である。
  GUIのプログラミングでは、それぞれのウィジェットを1つのクラスで表現する。

   class CheckButoon { ・・・・・チェックボタンのためのクラス
     …
   };

   class RadioButton { ・・・・・・ラジオボタンのためのクラス
     …
   };

   ただし、普通はこのようにしてそれぞれのGUI部品を関連の無い別のクラスにする事はしない。
  GUIの部品に共通する性質を抽出したクラスを作り、それぞれの部品はそこから派生して作る。

   class Widget { ・・・・・すべてのウィジェットの基本クラス
     …
   }

   全てのウィジェットは、このWidgetを祖先とする一族となる。これによち、多態性を利用できるようになる。

   class CheckButton extends Widget { ・・・・・Widgetから派生
     …
   }

   class RadioButton extends Widget { ・・・・・Widgetから派生
     …
   }

親クラスWidget

   親クラスWidgetには、全てのウィジェットが共通にもつメンバーを置く。例えば、

   (x, y) ・・・・・その部品の表示位置
   label ・・・・・その部品特有の文字列(例えばボタンなら表示する文字列)
   show( ) ・・・その部品を表示するためのメソッド

   本節で扱うウィジェットは座標を必要としないので、labelとshowをメンバーに持たせることにする。

   class Widget {
    String label; ・・・・・それぞれの部品が必要とする文字列

    void show( ) { ・・・・・自分自身を表示する
    }
   }

   これらのメンバーのほかにフィールドlabelを初期化するため、コンストラクタを用意する。

   class Widget {
    String label;

    Widget(String s) { ・・・・・コンストラクタ
     label = s;
    }

    void show( ) {
    }
   }

   これで大体Widgetクラスの完成であるが、次節で少し改良する。

抽象クラスと抽象メソッド

   本節では、インスタンスを生成する必要のないクラスを抽象クラスとして実装する方法を扱う。

抽象メソッド

   メソッドshowは、それぞれの部品によって処理内容が異なる。そこで、クラスWidgetの本体は空のままにしておく。

   void show( ) {
    ・・・・・ここには下位のクラスで必要な処理を書く
   }

   このように下位クラスのためだけに存在し、本体を必要としないメソッドは抽象メソッドにするとよい。
  抽象メソッドは、メソッドをキーワードabstractで修飾する。
  また、本体を必要としないので本体部分{ }を省略して、( )の次にいきなりセミコロンを書く。

   abstract void show( ); ・・・・・抽象メソッド

抽象クラス

   抽象メソッドを持つクラスは中傷クラスにしなければならない。抽象クラスは、クラス宣言をキーワードabstractで修飾する。

    abstract class Widget { ・・・・・抽象クラス
     String label;

    Widget(String s) {
     label = s;
    }

    abstract void show( ); ・・・・・抽象メソッド
   }

   抽象クラスは、下位クラスの為だけに存在するので、インスタンスを生成する事は出来ない。
  なお、抽象クラスに必ず抽象メソッドが必要なわけでない。
  メンバーとして普通のフィールドやメソッドを持つことが出来るし、もちろん抽象メソッドを含める事も出来る。

チェックボタン

   次に具体的なウィジェットとしてチェックボタンのためのクラスCheckButtonを考える。CheckButtonは、親クラスWeidgetを継承する。

   class CheckButton extends Widget {
   }

   チェックボタンは「押されている・押されていない」の2値状態を取るボタンである。
  そこで、この2値状態を表現するためのフィールドCheckを設ける。checkの型は、true/falseの2値をとるboolean型が適切である。

   class CheckButton extends Widget {
    boolean check; ・・・・・現在チェックされていればtrue、チェックされていなければfalse
   }

   checkを初期化するためのコンストラクタを設ける

   class CheckButton(String s) {
    check = false; ・・・・・初期状態はoff
   }

   引数のsは、チェックボタンのラベルである。このラベルはチェックボタンの脇に表示する。
  このsは親クラスWidgetにあるフィールドlabelに保存する。

   label = s;

   のように直接代入しても良い。しかし、せっかく親クラスのコンストラクタがこのコードを持っている。

   Widget(String s) { ・・・・・親クラスのコンストラクタ
    label = s;
   }

   そこで、親クラスのコンストラクタにこの処理を委託する。

   CheckButton(String s) {
    super(s); ・・・・・sをlabelに代入する
    check = false;
   }

   チェックボタンが押される度にボタンのon/offが変わる。そこで、pushというメソッドを設けてこの処理を行う。

   void push( ) {
    check = !check; ・・・・・ボタンのon/offを反転する
   }

   ちなみに!は、論理値を反転するための論理演算子である。
  最後に親クラスでは抽象メソッドであったshowの実態を定義する。さもないと、チェックボタンのためのインスタンスが生成できない。
  CheckButtonのshowは自身のチェックボックスを表示する。これは、次のようにラベルとボタンの押下状態を表示すればよい。

   void show( ) {
    System.out.println("チェックボタン:" + label);
    System.out.println("チェックの状態=" + check +"\n");
   }

   これでCheckButtonクラスの完成である。次にその全体像を掲げる。

   //チェックボタン---on/offの2値状態を取る
   class CheckButton extends Widget {
    boolean check;  //現在チェックされていればtrue

    //sはチェックボタンのラベル
    CheckButton(String s) {
     super(s);
     check = false;
    }

    //チェック状態を反転する
    void push( ) {
     check = !check;
    }

    void show( ) {
     System.out.println("チェックボタン:" + label);
     System.out.println("チェックの状態=" + check + "\n");
    }
   }

ラジオボタン

   次にラジオボタンのためのクラスRadioButtonを考える。RadioButtonも、親クラスWidgetを継承する。

   class RadioButton extends Widget {
   }

   ラジオボタンは、複数個のボタンのグループで、どれか1つだけを選択出来る。
  そこで、次の2つのフィールドでボタンの数とどのボタンが選択されているかを表現する。

   class RadioButton extends Widget {
    int max; ・・・・・ボタンの数
    int select; ・・・現在選択されているボタン
   }

   これらのフィールドをコンストラクタで初期化する。コンストラクタの引数は2つで、ラベルに使う文字列とボタンの数である。

   RadioButton(String s, int n) {
   }

   まず、親クラスのコンストラクタを呼び出してラベルの文字列を保存する。

   RadioButton(String s, int n) {
    super(s);
   }

   そして、ボタンの数をmaxにセットする。

   RadioButton(String s, int n) {
    super(s);
    max = n; ・・・・・ボタンの数
   }

   また、現在選択されているボタンの初期値を1(最初のボタン)にする。

   RadioButton(String s,int n) {
    super(s);
    max = n;
    select = 1; ・・・・・選択の初期値
   }

   最後にエラー処理を加味する。ボタンの数は最低でも1個は必要なので、nが0以下の場合は1にする。

   RadioButton(String s, int n) {
    super(s);

    if(n <= 0)
     max = 1; ・・・・・ボタンの数を最低でも1にする
    else
     max = n;

    select = 1;
   }

   これでコンストラクタの完成である。
   次にボタンの選択を変えるメソッドpushを作る。pushの引数は1つで、新しい選択を指定してもらう。

   void push(int n) { ・・・・・nは新しく選択されたボタン
   }

   このnをselectに代入すればよい。ただし、nが1以上max以下の場合は不正なボタンなので、その場合は何もしない。

   void push(int n) {
    if(0 < n && n <= max) ・・・・・正しいボタンなら
     select = n; ・・・・・・・・・・・・・選択を変える
   }

   最後にラジオボタンを表示するshowを実装する。これは、ラベルと現在表示されているボタンの番号を表示する。

   void show( ) {
    System.out.println("ラジオボタン:! + label);
    System.out.println("現在選択されているボタン=" + select + "\n");
   }

   これで、クラスRadioButtonの完成である。次にその全体像を示す。

   //ラジオボタン---どれか1つだけ選択出来る
   class RadioButton extends Widget {
    int max;     //ボタンの数
    int select;    //現在選択されているボタン

    //sはチェックボタンのラベル
    //nはボタンの数
    RadioButton(String s, int n) {
     super(s);

     if(n <= 0)
      max = 1;     //ボタンの数は最低でも1にする
     else
      max = n;

     select = 1;     //デフォルトではボタン1を選択
    }

    //ボタンを選択する
    //不正なボタンが選択された時は何もしない
    void push(int n) {
     if (0 < n && n <= max)
      select = n;
    }

    void show( ) {
     System.out.println("ラジオボタン:" + label);
     System.out.println("現在選択されているボタン=" + select + "\n");
     }
   }

コンテナ

   ウィジェットの最後としてウィンドウのためのクラスWindowを作成する。

   class Window extends Widget {
   }

   ウィンドウクラスは、他のウィジェットと違って少々複雑である。
  なぜならウィンドウは他の単純なウィジェットと違ってコンテナとしての機能を持っているからである。

ウィジェットの管理

   コンテナというのは、「入れ物」の事である。
  GUIで言うところのコンテナは、自分自身がウィジェットでありながら、他のウィジェットを自分の上に配置する事が出来る。
  そのようなウィジェットがコンテナである。
   ウィンドウはコンテナの一種である。したがって、その上にチェックボタンたラジオボタンを置く事が出来る。
  ウィンドウには、そのようなコンテナとしての仕組みを持たせなければならない。
   ここでは、他のウィジェットを管理するためのデータ構造として(プログラムを簡単にするために)配列を使う。その配列の型はWidgetである。

   class Window extends Widget {
    Widget widget[ ]; ・・・・・ウィジェットを管理するための配列
   }

   配列の型を単純にWidgetに出来るのは、多態性のおかげである。
  全てのウィジェットは、クラスWidgetの下位型なので、この配列に代入する事ができる。
  チェックボタンであろうと、ラジオボタンであろうと、どんなウィジェットでもこれ1本で管理する事が出来る。
   また、現在管理しているウィジェットの数をフィールドmaxに保存する。

   class Windpw extends Widget {
    Widget widget[ ];
    int max; ・・・・・現在のウィジェット数
   }

コンストラクタ

   必要なフィールドはこれだけである。次にこれらのフィールドを初期化するため、コンストラクタを作成する。
  引数は1つで、ラベル用の文字列を指定してもらう。
  なお、このラベルは、(仮想的なGUIでなく、ホンモノのGUIであったなら)おそらくウィンドウのタイトルに使われるであろう。

   Window(String s) {
    super(s); ・・・・・ラベルを保存
   }

   配列Widgetについては、そのインスタンスを生成する。
  ここでは10個までのウィジェットを管理するものとして、10個の配列要素を確保する。

   Window(String s) {
    super(s);
    widgets = new Widget[10]; ・・・・・配列要素を生成
   }

   初期状態では、まだ他のウィジェットを持っていないので、フィールドmaxの初期値は0である。

   Window(String s) {
    super(s);
    widgets = new Widget[10];
    max = 0; ・・・・・初期状態でウィジェット数は0
   }

ウィジェットの追加

   ウィンドウクラスのウィジェットを追加するためのメソッドaddを作る。addの引数は1つで、追加するウィジェットである。

   void add(Widget w) { ・・・・・引数の型がWidget
   }

   ここで引数の型がWidgetである事に注意。
  全てのウィジェットは、Widget型の下位型なので、Widget1つでどんなウィジェットでも表現する事が出来る。
  ここにも多態性の有難さが表れている。
   引数で指定されたウィジェットを配列Widgetの要素に追加していく。例えば、3個のウィジェットを追加すると次のようになる。

   Widgets[0] ・・・・・第1ウィジェット
   Widgets[1] ・・・・・第2ウィジェット
   Widgets[3] ・・・・・第3ウィジェット

   max = 3 ・・・・・・・現在のウィジェット数

   maxが現在のウィンドウ数を表しているので、ウィジェットを追加するためのコードは次のようになる。

   void add(Widget w) {
    widgets[max++] = w; ・・・・・ウィジェットを配列要素に代入すると同時にmaxをカウントアップ
   }

   ただし、maxはウィジェットの最大数10を超える事は出来ないので、次のように範囲のチェックを入れる。

   void add(widget w) {
    if (max < widgets.length)
     widgets[max++] = w;
   }

ウィンドウの表示

   最後にウィンドウ自身を表示するshowを実装する。

   class Window extends Widget {
   }

   ウィンドウの表示は、他のウィジェットと比べると少々複雑である。
  なぜなら自分自身の補かに管理しているウィジェット全てを表示しなければならないからである。
   しかし、これも多態性のおかげでウィジェットの種類については目をつぶる事ができる。
  管理しているのが、チェックボタンであるとか、ラジオボタンであるとかのような事は気にする必要が無い。
  どの配列要素にもWidget型のデータが代入されていると考えれば良い。ただ、そのshowメソッドを呼び出すだけである。
  後は多態性の仕組みが、正しいshowを呼び出してくれる。
  (その配列要素にチェックボタンが代入されていれば、チェックボタンのshowが呼び出される。)
   したがって、Windowのshowは、次のようなコードを実装すればよい。

   1: 自分自身を表示する
   2: 配列widgetsの先頭要素からmax個分のshowを呼び出す。

   1:の自分自身を表示する部分は、次のように書く事が出来る。

   void show( ) {
    System.out.println("ウィンドウ:" + label);
   }

   また、管理する子ウィジェットを表示する際に識別しやすいようにマイナス記号で罫線を出力する。

   void show( ) {
    System.out.println("ウィンドウ:" + label);
    System.out.println("------------------------------");
   }

   2:の配列widgetsの先頭要素からmax個分のshowを呼び出す部分は次のようになる。

   void show( ) {
     …

    for(int i = 0; i <max; i++)
     widgets[i].show( );
   }

   これで終わりである。次にshowの全体像を示す。

   void show( ) {
    System.out.println("ウィンドウ:" + label);
    System.out.println("------------------------------");

    for(int i = 0; i < max; i++)
     widgets[i].show( );
   }

   以上でウィンドウを表現するwindowクラスが完成した。次にその全体像を示す。

   //ウィンドウ---他の部品を最大10個まで載せる事ができる
   class Window extends Widget {
    Widget widgets[ ];   //管理する部品
    int max;         //現在の部品数

    window(String s) {
     super(s);
     widgets = new Widget[10];
     max = 0;
    }

    //部品を追加する
    //部品数が10を超える場合は何もしない
    void add(Widget w) {
     if (max<widgets,length)
      widgets[max++] = w;
    }

    //自分自身と管理している部品を表示する
    void show( ) {
     System,out.peintln("ウィンドウ;" + lanel);
     System.out.println("------------------------------");

     for (int i = 0; i < max; i++)
      widget[i].show( );
    }
   }

仮想GUIクラスのテスト

   以上、仮想GUIを実現するためのクラス軍を作成してきた。これらのクラスをテストする為のコードを書く。
  ここでは、ウィンドウを生成し。そこの2つのチェックボタンと1つのラジオボタンを配置する。
   まず、次がウィンドウを生成するコードである。

   Window w = new Window("擬似GUIアプリケーション");

   また、次が2つのチェックボタンを生成するコードである。

   CheckButton b1 = new CheckButton("オプション1");
   CheckButton b2 = new CheckButton("オプション2");

   メソッドpushをテストするため、チェックボタン2の方をプッシュしてみる。

   b2.push( );

   最後にラジオボタンを生成する。ボタンの数は3つにする。

   RadioButton r = new RadioButton("3連ボタン",3);

   ラジオボタンのpushメソッドもテストしておく。ここでは、2番目のノタンを押したことにしておく。

   r.push(2);

   そして、チェックボタンとラジオボタンをウィンドウに配置する。

   w.add(b1); ・・・・・チェックボタン1を配置
   w.add(b2); ・・・・・チェックボタン2を配置
   w.add(r); ・・・・・・ラジオボタンを配置

   最後にウィンドウを表示する。

   w.show( );

   以上をまとめると、次のようなmainメソッドが出来上がる。

   public class Gui {
    public static void main(String[ ] args) {
     window w = new Window("擬似GUIアプリケーション");

     CheckButton b1 = new CheckButton("オプション1");
     CheckButton b2 = new CheckButton("オプション2");
     b2.push( );

     RadioButton r = new RadioButton("3連ボタン",3);
     r.push(2);

     w.add(b1);
     w.add(b2);
     w.add(r);

     w.show( );
    }
   }

   前項で示した仮想GUIクラスのテストを含むサンプルアプリケーションを次に示す。

   サンプル:Gui.java

   //
   //Gui.java---簡単なGUIのシミュレーション
   //

   //部品クラスの起点
   abstract class Widget {
    String label;   //それぞれの部品が必要とする文字列

    Widget(String s) {
     label = s;
    }

    abstract void show( );   //それぞれの部品が自分自身を表示する
    }

    //チェックボタン--on/offの2値状態を取る
    class Checkbutton extends Widget {
    boolean check;   //現在チェックされていればtrue

    //sはチェックボタンのラベル
    CheckButton(String s) {
     super(s);
     check = false;
    }

    //チェック状態を反転する
    void push( ) {
     check = !check;
    }

    void show( ) {
     System.out.println("チェックボタン:" + label);
     System.out.println("チェックの状態=" + check + "\n");
    }
   }

   //ラジオボタン---どれか1つだけ選択できる
   class RadioButton extends Widget {
    int max;     //ボタンの数
    int select;   //現在選択されているボタン

    //sはチェックボタンのラベル
    //nはボタンの数
    RadioButton(String s, int n) {
     super(s);

     if(n <= )
      max = 1;   //ボタンの数は最低でも1にする
     else
      max = n;

     select = 1;   //デフォルトではボタン1を選択
    }

    //ボタンを選択する
    //不正なボタンが選択された時は何もしない
    void push(int n) {
     if (0 < n && n <= max)
      select = n;
    }

    void show( ) {
     System.out.println("ラジオボタン:" + label);
     System.out.println("現在選択されているボタン=" + select + "\n");
    }
   }

   //ウィンドウ---他の部品を最大10個まで乗せることが出来る。
   class Window extends Widget {
    Widget widgets[ ];   //管理する部品
    int max;         //現在の部品数

    Window(String s) {
     super(s);
     widgets = new Widget[10];
     max = 0;
    }

    //部品を追加する
    //部品数が10を超える場合は何もしない
    void add(Widget w) {
     if (max < widgets.length)
      widgets[maax++] = w;
    }

    //自分自身と管理している部品を表示する
    void show( ) {
     System.out.println("ウィンドウ:" + label);
     system.out.println("------------------------------");

     for(int i = 0; i < max; i++)
      widgets[i].show( );
    }
   }

   public class Gui {
    public static void main(String[ ]args) {
     Window w = new Window(W擬似GUIアプリケーション");

     CheckButton b1 = new CheckButton("オプション1");
     CheckButton b2 = new CheckButton("オプション2");
     b2.push( );

     RadioButton r = new RadioButton("3連ボタン",3);
     r.push(2);

     w.add(b1);
     w.add(b2);
     w.add(r);

     w.show( );
    }
   }

   実行例

   $ java Gui
   ウィンドウ;擬似GUIアプリケーション
   ------------------------------
   チェックボタン:オプション1
   チェックの状態=false

   チェックボタン:オプション2
   チェックの状態=true

   ラジオボタン:3連ボタン
   現在選択されているボタン=2

   $ _

次へ