クラスをパラメータとして渡して、インスタンス化する

コンストラクタの呼び出しには、クラス参照型の変数を用いることが出来ます。 クラス参照型変数を用いてコンストラクタを呼び出すと、コンパイル時にその型がわからないインスタンスを生成することが可能となります。 ここでは実際に、このクラス参照型のパラメータをもつメソッドを作成して、 インスタンス生成をみてみましょう。

サンプルプログラムは、以下のようになります。

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 メソッドでインスタンスの 破棄を実行しています。

TClass について

クラス参照型については、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 が全てのクラスの親クラス(スーパークラス)ということになります。


up next
Last update: 2003/2/7