コンストラクタの呼び出しには、クラス参照型の変数を用いることが出来ます。 クラス参照型変数を用いてコンストラクタを呼び出すと、コンパイル時にその型がわからないインスタンスを生成することが可能となります。 ここでは実際に、このクラス参照型のパラメータをもつメソッドを作成して、 インスタンス生成をみてみましょう。
サンプルプログラムは、以下のようになります。
Unit2 について
unit Unit2;
interface
uses SysUtils, StrUtils;
type
TBase = class(TObject)
private
fStr: string;
protected
function Process: string; virtual; abstract;
public
constructor Create(str: string);
procedure Print;
property Str: string read fStr;
end;
TDefault = class(TBase)
protected
function Process: string; override;
end;
TUpper = class(TBase)
protected
function Process: string; override;
end;
TReverse = class(TBase)
protected
function Process: string; override;
end;
TBaseClass = class of TBase;
TFactory = class(TObject)
public
class function CreateStr(BaseClass: TBaseClass; str: string): TBase;
end;
implementation
uses Unit1;
{ TBase }
constructor TBase.Create(str: string);
begin
fStr := str
end;
procedure TBase.Print;
begin
Form1.Memo1.Lines.Add(Process);
end;
{ TDefault }
function TDefault.Process: string;
begin
Result := Str;
end;
{ TUpper }
function TUpper.Process: string;
begin
Result := UpperCase(Str);
end;
{ TReverse }
function TReverse.Process: string;
begin
Result := ReverseString(Str);
end;
{ TNewFactory }
class function TFactory.CreateStr(BaseClass: TBaseClass;
str: string): TBase;
begin
Result := BaseClass.Create(str);
end;
end.
Unit1 について
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
Button4: TButton;
Button5: TButton;
Button6: TButton;
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses Unit2;
procedure TForm1.Button4Click(Sender: TObject);
var
Default: TBase;
begin
Default := TFactory.CreateStr(TDefault, 'Hello');
Default.Print;
Default.Free;
end;
procedure TForm1.Button5Click(Sender: TObject);
var
Reverse: TBase;
begin
Reverse := TFactory.CreateStr(TReverse, 'Hello');
Reverse.Print;
Reverse.Free;
end;
procedure TForm1.Button6Click(Sender: TObject);
var
Upper: TBase;
begin
Upper := TFactory.CreateStr(TUpper, 'Hello');
Upper.Print;
Upper.Free;
end;
end.
コンパイルし、実行しますと図のように引数として渡された文字列"Hello"が 実際に生成されるインスタンスに応じて、異なる処理が行われ、 Memo に表示 されています。
Unit2 について見ていきましょう。
TBase = class(TObject)
private
fStr: string;
protected
function Process: string; virtual; abstract;
public
constructor Create(str: string);
procedure Print;
property Str: string read fStr;
end;
TDefault = class(TBase)
protected
function Process: string; override;
end;
TUpper = class(TBase)
protected
function Process: string; override;
end;
TReverse = class(TBase)
protected
function Process: string; override;
end;
まず、TBase クラスでコンストラクタと 2 つのメソッド(Print, Process)が 定義されています。Process メソッドは、仮想メソッドであり、且つ抽象メソッドとして定義されています。つまり、このメソッドは TBase クラスの サブクラス(子クラス)に実装がまかされていることになります。 また、Process メソッドのアクセス制御を protected にしたのは、 このクラスと、このクラスから派生するサブクラスだけが、このメソッドを利用できるようにするためです(厳密には、protected で 定義されるメソッドは、サブクラスが定義されているユニットのコードからもアクセスできます)。
TBase クラスのサブクラスである TDefault, TUpper, TReverse クラスを 見ていきましょう。それぞれのクラスでは Process メソッドを次のように実装しています。
{ TDefault }
function TDefault.Process: string;
begin
Result := Str;
end;
{ TUpper }
function TUpper.Process: string;
begin
Result := UpperCase(Str);
end;
{ TReverse }
function TReverse.Process: string;
begin
Result := ReverseString(Str);
end;
TDefault クラスでの Process メソッドでは、プロパティ Str に何も処理を施さず、そのまま呼び出し元に 返しています。TUpper クラスでの Process メソッドは、Str を UpperCase 関数に渡して、全ての文字を 大文字にしてから、呼び出し元に返しています。TReverse クラスでは、ReverseString 関数で、文字列を逆にしてから呼び出し元に返しています。
TBaseClass = class of TBase;
上記のコードは、クラス参照型 TBaseClass を定義しています。 クラス参照とは、単にクラス型の参照です。つまり、TBaseClass クラス参照型の変数が TBase とそのサブクラスを参照できるということです。
TFactory = class(TObject) public class function CreateStr(BaseClass: TBaseClass; str: string): TBase; end;
上記のコードでは TFactory クラスを定義し、クラスメソッド CreateStr を 定義しています。このようにクラスメソッドを定義する場合には、予約語 class を先頭につけます。
クラスメソッドとは、オブジェクトに適用するメソッドではなく、 クラスを操作するメソッドです。ですから、CreateStr クラスメソッドを 呼び出す際に、TFactory をインスタンス化する必要はありません(してはいけない ということではありません)。 また、クラスメソッドは、クラスのフィールドにはアクセスできないことに注意してください。つまり、クラスメソッドは特定のインスタンスに適用されるのではなく、 クラス全体に適用されるメソッドということになります。
class function TFactory.CreateStr(BaseClass: TBaseClass; str: string): TBase; begin Result := BaseClass.Create(str); end;
CreteStr クラスメソッドの内容をみてみますと、クラス参照を用いて コンストラクタを呼び出しています。
それでは、これらのクラスを使用したコードを見てみましょう。
procedure TForm1.Button4Click(Sender: TObject); var Default: TBase; begin Default := TFactory.CreateStr(TDefault, 'Hello'); Default.Print; Default.Free; end; procedure TForm1.Button5Click(Sender: TObject); var Reverse: TBase; begin Reverse := TFactory.CreateStr(TReverse, 'Hello'); Reverse.Print; Reverse.Free; end; procedure TForm1.Button6Click(Sender: TObject); var Upper: TBase; begin Upper := TFactory.CreateStr(TUpper, 'Hello'); Upper.Print; Upper.Free; end;
TFactory のクラスメソッド CreateStr にインスタンス化するクラス型と 文字列を渡しています。クラスメソッド CreateStr では、渡されたクラス型の インスタンスを生成し、その参照を返します。
そして、Print メソッドを呼び出し、Free メソッドでインスタンスの 破棄を実行しています。
クラス参照型については、Delphi で次のような定義を見る事が出来ます。
TClass = class of TObject;
Delphi 6 では System.pas で上記のコードが定義されています。この TClass は、 TObject 型を参照できるということですので、Delphi で定義される クラスは、全て参照できるということになります。
この TClass を用いたサンプルを見てみましょう。
procedure TForm1.Button1Click(Sender: TObject);
var
aClass: TClass;
begin
ListBox1.Items.Add(ClassName);
aClass := ClassParent;
while aClass <> nil do
begin
ListBox1.Items.Add(aClass.ClassName);
aClass := aClass.ClassParent;
end;
end;
このプログラムは、最初に自分のクラス名(TForm1)を 表示してから、その親クラス(スーパークラス)名を 表示し、さらにその親クラスの親クラスへと順にクラス名を表示させるプログラムです。
最後は、TObject と終了しているのが分かると思います。 これは、TForm1 クラスに限らずどんなクラスでも継承をさかのぼると、最後には TObject にたどり着きます。 つまり、TObject が全てのクラスの親クラス(スーパークラス)ということになります。