2011-09-28 8 views
7

クラスのインスタンスを作成するクラス関数を持つことができます:Delphiでインスタンスを作成するクラス関数

TMyClass = class(TSomeParent) 
public 
    class function New(AValue : integer) : TMyClass; 
end; 

TDerivedClass = class(TMyClass) 
public 
    function Beep; 
end; 

... 
var 
    myList : TList<T>; 
    item : TDerivedClass; 
begin 
    myList.Add(TDerivedClass.New(1)) 
    myList.Add(TDerivedClass.New(3)) 
    myList.Add(TDerivedClass.New(5)) 

    for item in myList do 
    item.Beep; //times the count in the class function 
... 

もしそうなら、その関数コードはどのように見えますか? TObjectのNewInstanceメソッドを使用し、派生クラスごとに毎回再実装しますか?コンストラクタを使用する方が安全かどうかですか?

これは、コマンド・パターンでこのアプローチを使用し、クラス・タイプとレシーバーを持つコマンド・リストをロードすることです。

//FYI: document is an instance of TDocument 
commandList.Execute(TOpenDocument(document)); 
commandList.Execute(TPasteFromClipboard(document)); 
//... lots of actions - some can undo 
commandList.Execute(TPrintDocument(document)); 
commandList.Execute(TSaveDocument(document)); 

これは、いくつかのコマンドがtext /実行時に解決する必要があります。

答えて

2

クラスのインスタンスを作成するクラス関数を使用できますか。
コンストラクタを使用する方が安全ですか?

コンストラクタは、クラスのインスタンスを作成するクラス関数です。 はただ置く:

constructor New(); virtual; 

そして、あなたが行ってもいいです。

virtual;部分は、すべての子孫クラスに対して同じNew()コンストラクタを呼び出すことができます。

+0

Createではなく、Newをコンストラクターと呼ぶ理由はなく、仮想である必要はありません。 –

+0

@Rob:クラス変数を使って構築するときに、コンストラクタをオーバーライドする理由があります。私はCreateがはるかに優れた名前であることに同意します。 New()は、クラス関数の名前としてNew()が与えられている質問に関連するものです。 – dmajkic

+0

仮想コンストラクタはクラス参照変数が使用されている場合にのみ有効ですが、仮想コンストラクタが必須*でないことは事実です。仮想コンストラクタは、子孫クラスでオーバーライドする必要がある場合にのみ必要です。それは他の仮想メソッドの使用と変わりません。この質問は、子孫が基本クラスとは異なる構築動作を必要とすることを示すものではありませんでした(別のクラスを構築する以外は自動ですが)。 –

12

ファクトリパターンと呼ばれています。それはDelphiで行うことができます。 VCLがフォームのデシリアライズを行う方法です。あなたが欠けているのは、システムの登録/参照部分です。

  • どこかに、登録テーブルを設定します。 Delphi XEをお使いの場合は、TDictionary<string, TMyClassType>として実装することができます。TMyClassTypeclass of TMyClassと定義されています。これは重要。クラス名とクラス型参照の間のマップが必要です。
  • TMyClassに仮想コンストラクタを配置します。それから降りてくるすべてのものは、このコンストラクタを使用するか、ファクトリパターンがそのコンストラクタを作成するときにそのコンストラクタをオーバーライドします。
  • 新しい子孫クラスを作成するときに、それ自身を登録テーブルに登録するメソッドを呼び出させます。これは、プログラムの起動時に、の初期化またはクラスコンストラクタのいずれかで発生する必要があります。

スクリプトから何かをインスタンス化する必要があり、このようにそれを実行します。

class function TMyClass.New(clsname: string; [other params]): TMyClass; 
begin 
    result := RegistrationTable[clsName].Create(other params); 
end; 

あなたは、クラス名からクラス参照を取得するために登録テーブルを使用し、上の仮想コンストラクタを呼び出しますクラス参照を使用して、正しいタイプのオブジェクトを取得します。

+1

優秀な説明 – RBA

+0

うわー。単純な問題を強調する方法について話してください! –

+1

@Rob:ハァッか。これは、「実際の型がスクリプトに由来する基本型から派生したオブジェクトを作成して」動作させることができる最も簡単な方法です。 –

0

コンストラクタ(クラス関数の特別な「種類」)を使用する必要があります。特別なメモリ割り当てが必要な場合を除き、TObject.NewInstanceは適切なオプションではありません。

コマンドリストのExecuteルーチンについて:関連する操作は、オブジェクトの種類によって異なります。ドキュメントを開いたり、印刷したり、貼り付けたり、保存したりすることができます(これは奇妙な前提ではありません)。この構造では実装が難しいでしょう。代わりに、実際にはすべて1つのドキュメントインスタンスのアクションである可能性のあるインターフェイス(IOpenDocument、IPasteFromClipboard、IPrintable、ISaveDocument)を追加することを検討してください。

5

はい、単に例えば、実際のコンストラクタを呼び出し、それが作成されますインスタンスを返す、クラスメソッドからインスタンスを作成することは技術的には可能である:、あなただけオフに優れている

type 
    TMyClass = class(TSomeParent) 
    public 
    constructor Create(AValue : Integer); virtual; 
    class function New(AValue : integer) : TMyClass; 
    end; 

    TDerivedClass = class(TMyClass) 
    public 
    constructor Create(AValue : Integer); override; 
    function Beep; 
    end; 

constructor TMyClass.Create(AValue : Integer); 
begin 
    inherited Create; 
    ... 
end; 

function TMyClass.New(AValue : integer) : TMyClass; 
begin 
    Result := Create(AValue); 
end; 

constructor TDerivedClass.Create(AValue : Integer); 
begin 
    inherited Create(AValue); 
    ... 
end; 

var 
    myList : TList<TMyClass>; 
    item : TMyClass; 
begin 
    myList.Add(TDerivedClass.New(1)) 
    myList.Add(TDerivedClass.New(3)) 
    myList.Add(TDerivedClass.New(5)) 
    for item in myList do 
    TDerivedClass(item).Beep; 

た場合

type 
    TMyClass = class(TSomeParent) 
    end; 

    TDerivedClass = class(TMyClass) 
    public 
    constructor Create(AValue : Integer); 
    function Beep; 
    end; 

var 
    myList : TList<TDerivedClass>; 
    item : TDerivedClass; 
begin 
    myList.Add(TDerivedClass.Create(1)) 
    myList.Add(TDerivedClass.Create(3)) 
    myList.Add(TDerivedClass.Create(5)) 
    for item in myList do 
    item.Beep; 
1

RTTIを使用することもできます。以下のコードは、アイテムのサブセットを持つオブジェクトの新しいインスタンスを取得する方法としてクラス内の通常のメソッドとして実行されますが、アイテム(リストオブジェクト自体と一緒に)はおそらく子孫オブジェクトであるため、メソッドが定義されているオブジェクトは、同じ型のインスタンスである必要があるため、十分ではありません。

私はそれが少しOTTかもしれいくつかのもののために感謝しますが、上記の設計では、それが動作

Function TParentList.GetSubRange(nStart,nEnd : Integer) : TParentList; 
var 
    aContext: TRttiContext; 
    aType: TRttiType; 
    aInsType : TRttiInstanceType; 
    sDebug : String; 
begin 
    aContext := TRttiContext.Create; 
    aType := aContext.GetType(self.ClassType); 
    aInsType := aType.AsInstance; 
    Result := aInsType.GetMethod('Create').Invoke(aInsType.MetaclassType,[]).AsType<TParentList>; 
    sDebug := Result.ClassName; // Should be TChildList 

    // Add the items from the list that make up the subrange. 
End; 

... GetSubRangeのようなものであるかどう

TParentItem = Class 
End; 

TParentList = Class 
    Items : TList<TParentItem>; 
    Function GetSubRange(nStart,nEnd : Integer) : TParentList; 
End; 

TChildItem = Class(TParentItem) 
end 

TChildList = Class(TParentList) 
end 

List := TChildList.Create; 
List.LoadData; 

SubList := List.GetSubRange(1,3); 

実装すなわちそれはクラスメソッドではなく、私が感謝しますが、もう一つの選択肢です。

関連する問題