今回は「オブジェクト指向」(というよりコンポーネント指向か)的なプログラミングの実験としてDelphiの「コンポーネント」を作ってみます。このコンポーネントはGUIの部品を単位にまとめる事ができるので、表示・管理するデータやその操作との関連性もイメージしやすいでしょう(GUI環境ではGUI部品に対するユーザーの操作というメッセージへの応答としてデータの処理を行う)。特に、最近は「オブジェクト」というよりは「コンポーネント」指向的なものが広く利用されるようになってきたので、コンポーネントというものはどんなものなのか、このあたりでじっくり考えてみたい、という事もあります。
というわけで、今回は「コンポーネント指向」プログラミングの実験として「出納帳」を作ってみる事にしました。
まず、全体の設計を考えてみます。今回はWindowsアプリケーションとして作るので、メインウインドウを持ったアプリケーション本体が必要ですね。このアプリケーションのメインウインドウ上に出納帳コンポーネントをおいて、ユーザーはこのメインウインドウを通して出納帳アプリケーション全体に対する終了や保存・読み込みなどの操作を行うようにします。ただし、当然の事ながら保存など出納帳に関する操作を実際に行うのは出納帳自身で、アプリケーションはユーザーの操作に応じて出納帳コンポーネントにメッセージを送るだけです。出納帳コンポーネントは入出力や表示を行い、コンポーネント自身が金銭データを持って計算・保存などの操作を行うようにします。
イメージとしては、メインウインドウ内に出納帳コンポーネントを貼り付けるような感じでしょうか。そして、ユーザーは出納帳コンポーネントに入力し、アプリケーションのメニューで保存やアプリケーションの終了を行うとアプリケーションがそのメッセージを出納帳コンポーネントに伝えて出納帳コンポーネントの方で必要な処理を行う、と。
出納帳コンポーネントには、データの入出力を行う出納帳本体を中心に合計額などを表示する領域もほしいですね。となると、コンポーネントはコンテナ的性質を持つTPanelから派生させるのが良いでしょう。ここに、データ入出力用のStringGridや各種のデータ表示用のパネルなどを置いていきます。また、コンポーネントを定義するクラス内のフィールドに金銭の出納データやデータ操作関数も定義します。これで、データの入出力や保持・操作を総合的に行える「出納帳コンポーネント」ができるはずです。
コンポーネントの作成は簡単です。コンポーネントはクラスで定義するので、作ろうとしているコンポーネントの元になりそうなクラスに独自に機能を付け足していくだけで思い通りのコンポーネントができてしまいます。Delphiのコンポーネントにはすでに基本的なイベントやプロパティが定義されているので、ウインドウの基本的な操作に関しては自分で書く必要はあまりないでしょう。
今回の出納帳コンポーネントは、TPanelから派生させたクラスをアプリケーション本体とは別のユニットで定義します。このユニットに、コンストラクタを始めとするメソッド・関数やデータを定義していくわけです。
では、早速コンポーネントを作ってみましょう。まず、新規のアプリケーションプロジェクトを作成したら、コンポーネントのクラスを定義するためのユニットを「新規作成」の「ユニット」で作ってください。
ユニットを作ったら、Classes, ExtCtrlsをUses節に加えてクラス定義で参照するクラスを利用できるようにします。
uses Classes,ExtCtrls;
次にコンポーネントの定義。とりあえず、TPanelを継承しただけのコンポーネントをフォームに貼り付けてみましょうか。以下のようにコンストラクタのみを定義したクラスを書いてみました。
unit Unit2; interface uses Classes,ExtCtrls; type TNote=class(TPanel) public constructor Create(AOwner:TComponent);override; end; implementation constructor TNote.Create(AOwner:TComponent); begin inherited Create(AOwner); // 親クラスのCreate実行 Width:=128; Height:=128; end;
これで、TPanelを継承した128×128ピクセルのパネルコンポーネントができました。このコンポーネントを利用するには、アプリケーションの方のユニット(unit1)のuses節にコンポーネントユニット(unit2)を追加し、private部にTNote型変数ntNoteを宣言します。そして、アプリケーションのOnCreateイベントハンドラでTNoteのコンストラクタを呼び出してntNoteオブジェクトを生成しましょう。
unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,unit2; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); private ntNote:TNote; { Private 宣言 } public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); begin ntNote:=TNote.Create(Self); ntNote.Parent:=Self; end; end.
次に、StringGridを加えてみます。TStringGrid型変数sgGridsを宣言し、コンポーネントのコンストラクタでコンポーネント自身をオーナーとして生成しましょう。ついでに、コンポーネント自身の大きさを変更し、コンポーネント内のストリンググリッドの大きさも調整するsetSizeメソッドとデストラクタも定義しておきました。
unit Note; interface Uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls,Grids; type TNote=class(TPanel) private sgGrids:TStringGrid; public constructor Create(AOwner:TComponent);override; procedure setSize(w,h:Integer); destructor Destroy;override; end; implementation constructor TNote.Create(AOwner:TComponent); // コンストラクタ begin inherited Create(AOwner); // 親クラスのCreate実行 Ctl3D:=False; BorderStyle:=bsSingle; sgGrids:=TStringGrid.Create(Self); // StringGrid作成 sgGrids.Parent:=Self; sgGrids.BorderStyle:=bsNone; sgGrids.Left:=0; sgGrids.Top:=32; sgGrids.ColCount:=7; sgGrids.RowCount:=50; sgGrids.FixedCols:=0; end; procedure TNote.setSize(w,h:Integer); begin Width:=w; Height:=h; sgGrids.Width:=Width-2; sgGrids.Height:=Height-64; end; destructor TNote.Destroy; // デストラクタ begin sgGrids.Free; inherited Destroy; end; end.
さあ、これで出納帳コンポーネントの原型ができたので、後は機能を追加して出納帳に仕上げていくだけです。