Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi unterschiedliche Klassen variabel instanzieren (https://www.delphipraxis.net/161797-unterschiedliche-klassen-variabel-instanzieren.html)

haentschman 21. Jul 2011 08:30

unterschiedliche Klassen variabel instanzieren
 
Guten Morgen... :hi:

es ist wahrscheinlich einfacher als gedacht aber ich quäle mich schon seit gestern durch die DP Suche ohne wirklich die Frage beantwortet zu bekommen.

Ziel:
- Klasse Test wird erzeugt mit Übergabeparameter
- Klasse Test erzeugt Klasse entsprechend dem Übergabeparameter als Property
Beispiel:
bei 1 --- Klasse Test1
bei 2 --- Klasse Test2
.
.
.

den Übergabeparameter wollte ich über eine Case Struktur auswerten und entsprechend erzeugen. Da müßte die Varable ja erst in im case deklariert werden... was ja nicht geht.

Ob Klasse Test1 von Test abgeleitet ist oder eigenständig ist mir relativ egal.

Bitte helft mir in die Richtung... Danke :hi:

Deep-Sea 21. Jul 2011 08:35

AW: unterschiedliche Klassen variabel instanzieren
 
Ich seh jetzt gerade das Problem nicht, sry :cyclops:
Und warum müsste eine Variable erst im Case deklariert werden?

himitsu 21. Jul 2011 09:03

AW: unterschiedliche Klassen variabel instanzieren
 
Das warum ist mir auch noch etas unklar,


aber es stimmt. Ob wohl ein Objekt eigentlich auch "nur" ein Record mit Zeiger ist, funktioniert Case dort nicht,
allerdings
Delphi-Quellcode:
TImageListLoader = class
  data: record
    case boolean of
      false: ();
      true: ();
  end;
end;

_Sebastian_ 21. Jul 2011 09:20

AW: unterschiedliche Klassen variabel instanzieren
 
Moin. Ich weiß nicht es was für dich ist aber ich habe mir mal ein Konstrukt gebaut wo ich dynamisch, variabel gewählte Klasse, erzeugt habe.
Ich habe das so gelöst. (etwas einfacher hier)

Die Klasse TAction bekommt eine Klassenreferenz übergeben und erzeugt sich daraus ein Objekt.


Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TBase = class(TObject)
  public
    procedure Action; virtual; abstract;
  end;

  TSpecial1 = class(TBase)
  public
    procedure Action; override;
  end;

  TSpecial2 = class(TBase)
  public
    procedure Action; override;
  end;

  TBaseClass = class of TBase;

  TAction = class(TObject)
  public
    constructor Create(aClass: TBaseClass);
  end;

 TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TSpecial1.Action;
begin
  showmessage('Action 1');
end;

procedure TSpecial2.Action;
begin
  showmessage('Action 2');
end;

constructor TAction.Create(aClass: TBaseClass);
var c : TBase;
begin
  c := aClass.Create;
  c.Action;
  c.Free;

end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  TAction.Create(TSpecial1);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  TAction.Create(TSpecial2);
end;

end.
gruß
Sebastian

haentschman 21. Jul 2011 11:02

AW: unterschiedliche Klassen variabel instanzieren
 
Danke für Eure Antworten...

ich kann mich im Moment leider nicht damit beschäftigen und näher darauf eingehen... Es gibt Dinge die (leider) wichtiger sind. Ich melde mich wenn ich die Zeit gefunden habe.

:hi:

Memnarch 21. Jul 2011 11:06

AW: unterschiedliche Klassen variabel instanzieren
 
öhm, wasn das für ne MemoryLeakMaschine?

Code:
procedure TForm1.Button2Click(Sender: TObject);
begin
  TAction.Create(TSpecial2);
end;

_Sebastian_ 21. Jul 2011 11:22

AW: unterschiedliche Klassen variabel instanzieren
 
Zitat:

Zitat von Memnarch (Beitrag 1112861)
öhm, wasn das für ne MemoryLeakMaschine?

Ups.. :roll:
Denke dem geneigten Entwickler wäre es aufgefallen bzw. nutzt den code eh etwas umfangreicher. ..

Code:
procedure TForm1.Button2Click(Sender: TObject);
var a:TAction;
begin
  a := TAction.Create(TSpecial2);
  a.free;
end;

himitsu 21. Jul 2011 12:08

AW: unterschiedliche Klassen variabel instanzieren
 
Man könnte die Klassen auch von TComponent ableiten und die Form als Owner mitgeben ... dann räumt die Form auf.


Oder :oops:
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
begin
  TAction.Create(TSpecial2).Free;
end;

Deep-Sea 21. Jul 2011 12:27

AW: unterschiedliche Klassen variabel instanzieren
 
Und wenn wir schon wieder so kleinlich sind, dann fehlt im Konstruktor von TAction ein Try-finally-Block:
Delphi-Quellcode:
constructor TAction.Create(aClass: TBaseClass);
var c : TBase;
begin
  c := aClass.Create;
  try
    c.Action; // Könnte ja eine Exception auswerfen ...
  finally
    c.Free;
  end;
end;
:stupid:

FredlFesl 21. Jul 2011 16:53

AW: unterschiedliche Klassen variabel instanzieren
 
Hmmm... Die Eingangsfrage beschäftigt sich mit einer Classfactory, wenn ich nicht irre.

Und nun wird klein-klein über unwichtige Dinge philosophiert.

So ein Resourcenschutzblock ist ja ganz nett, aber im allgemeinen überflüssig wie ein Kropf.
Ausnahmen sind knappe Resourcen (z.B. von Windows), die in einer unteren Schicht angefordert und auch im Fehlerfall wieder freigegeben werden müssen. Dann kümmert sich jedoch (z.B.) ein Securitymanager um die Exception.

Bei einem Buttonklick kann man natürlich auch einen Resourcenschutzblock anwenden, aber wozu? Wenn's knallt, kann ich sowieso noch nicht ausliefern...

haentschman 21. Jul 2011 18:58

AW: unterschiedliche Klassen variabel instanzieren
 
Nochmal danke an alle... 8-)

ich habe mal versucht die obigen Tipps umzusetzen. Im Moment habe ich entnervt aufgegeben. Ich denke langsam über eine Designumstellung nach, da ich glaube was ich vorhabe geht nicht.
Ich erkläre mein Ziel noch mal anders.

- ich benötige eine Variable die die Instanz eines Objektes enthällt
- je nach Wert eines Integer soll das entsprechende Objekt in der Variable abgelegt werden
- die Objekte sind zwar von einer Basis abgeleitet haben aber unterschiedliche Properties etc.

Normalerweise wird ja die Variable mit der Klasse deklariert. Nur wie deklariere ich die wenn nicht klar ist welches Objekt da mal rein soll ? Damit kann ich auch nicht auf die Properties zugreifen.

Wo ist der Denkfehler ?

Bjoerk 21. Jul 2011 20:57

AW: unterschiedliche Klassen variabel instanzieren
 
Meinst du so ?

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TKlasse = class(TObject)
  public
    constructor Create(const ATyp: integer); overload;
    constructor Create; overload;
  end;

  TKlasse1 = class(TKlasse)
    function MachWas: string;
  end;

  TKlasse2 = class(TKlasse)
    function MachWas: string;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Machwas(const ATyp:integer);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Machwas(1);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Machwas(2);
end;

procedure TForm1.Machwas(const ATyp:integer);
var
  Klasse: TKlasse;
begin
  if ATyp=1 then
  begin
    Klasse:=TKlasse.Create(ATyp);
    try
      with Klasse as TKlasse1 do ShowMessage(Machwas);
    finally
      Klasse.Free;
    end;
  end;
  if ATyp=2 then
  begin
    Klasse:=TKlasse.Create(ATyp);
    try
      with Klasse as TKlasse2 do ShowMessage(Machwas);
    finally
      Klasse.Free;
    end;
  end;
end;

constructor TKlasse.Create;
begin
  inherited Create;
end;

constructor TKlasse.Create(const ATyp: integer);
begin
  if ATyp=1 then Self:= TKlasse1.Create;
  if ATyp=2 then Self:= TKlasse2.Create;
end;

function TKlasse1.MachWas: string;
begin
  Result:= 'TKlasse1.MachWas';
end;

function TKlasse2.MachWas: string;
begin
  Result:= 'TKlasse2.MachWas';
end;

end.

stahli 21. Jul 2011 23:22

AW: unterschiedliche Klassen variabel instanzieren
 
Falls Dir das etwas hilft, ich caste in meiner Klasse auf unterschiedliche Property-Klassen.
Sieh Dir mal Data_Create.. und Data_Is... an. In "Data" können unterschiedliche Objekte verwaltet werden.
Die Unit wird von einem Experten erstellt. Die Regionen enthalten die automatisch erzeugten Quelltextabschnitte.

Bei der Verfahrensweise müssen halt für alle möglichen Klassen entsprechende Methoden eingerichtet werden (in meinem Fall macht der Experte das allerdings autiomatisch).
In der Anwendung funktioniert das tadellos.


Delphi-Quellcode:
unit odTournament;

interface

uses
  Classes, SysUtils, od, odOlympicCustom, gOlympic, odPerson, odCourt
  {$REGION   'USES'}, odMelee, odSwiss, odGroup, odKo, odGroupKo{$ENDREGION 'USES'};

type

  TodTournament = class(TodOlympicCustom)
  private
{$REGION   '   PRIVAT'}
{$REGION   '   name'}
    FName: String;
{$ENDREGION '   name'}
{$REGION   '   activate'}
    FActivate: Boolean;
{$ENDREGION '   activate'}
{$REGION   '   excludecourts'}
    FExcludeCourts: String;
{$ENDREGION '   excludecourts'}
{$REGION   '   data'}
    FData: Tod;
{$ENDREGION '   data'}
{$ENDREGION '   PRIVAT'}
  protected
{$REGION   '   PROTECTED'}
{$REGION   '   name'}
    function get_Name: String; virtual;
    procedure set_Name(const Value: String); virtual;
{$ENDREGION '   name'}
{$REGION   '   activate'}
    function get_Activate: Boolean; virtual;
    procedure set_Activate(const Value: Boolean); virtual;
{$ENDREGION '   activate'}
{$REGION   '   excludecourts'}
    function get_ExcludeCourts: String; virtual;
    procedure set_ExcludeCourts(const Value: String); virtual;
{$ENDREGION '   excludecourts'}
{$ENDREGION '   PROTECTED'}
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function ExistPerson(odPerson: TodPerson): Boolean;
    function GetFreeCourtForTournament(DoubleFlag: Boolean): TodCourt;
    function TournamentTypeName: String;
{$REGION   '   PUBLIC'}
{$REGION   '   data_Melee'}
    procedure Data_Create_Melee;
    procedure Data_Free_Melee;
    function Data_Is_Melee: Boolean;
    function Melee: TodMelee;
{$ENDREGION '   data_Melee'}
{$REGION   '   data_Swiss'}
    procedure Data_Create_Swiss;
    procedure Data_Free_Swiss;
    function Data_Is_Swiss: Boolean;
    function Swiss: TodSwiss;
{$ENDREGION '   data_Swiss'}
{$REGION   '   data_Group'}
    procedure Data_Create_Group;
    procedure Data_Free_Group;
    function Data_Is_Group: Boolean;
    function Group: TodGroup;
{$ENDREGION '   data_Group'}
{$REGION   '   data_Ko'}
    procedure Data_Create_Ko;
    procedure Data_Free_Ko;
    function Data_Is_Ko: Boolean;
    function Ko: TodKo;
{$ENDREGION '   data_Ko'}
{$REGION   '   data_GroupKo'}
    procedure Data_Create_GroupKo;
    procedure Data_Free_GroupKo;
    function Data_Is_GroupKo: Boolean;
    function GroupKo: TodGroupKo;
{$ENDREGION '   data_GroupKo'}
{$REGION   '   name'}
    [AttrOd]
    property Name: String read get_Name write set_Name;
{$ENDREGION '   name'}
{$REGION   '   activate'}
    [AttrOd]
    property Activate: Boolean read get_Activate write set_Activate;
{$ENDREGION '   activate'}
{$REGION   '   excludecourts'}
    [AttrOd]
    property ExcludeCourts: String read get_ExcludeCourts write set_ExcludeCourts;
{$ENDREGION '   excludecourts'}
{$REGION   '   data'}
    [AttrOd]
    property Data: Tod read FData;
{$ENDREGION '   data'}
{$ENDREGION '   PUBLIC'}
  published
  end;

implementation

uses
  pOlympic, odTournamentEvent;

// TodTournament

constructor TodTournament.Create(AOwner: TComponent);
begin
  inherited;
{$REGION   ' CREATE'}
{$REGION   ' data'}
  FData := nil;
{$ENDREGION ' data'}
{$ENDREGION ' CREATE'}
end;

destructor TodTournament.Destroy;
begin
  inherited;
end;

function TodTournament.ExistPerson(odPerson: TodPerson): Boolean;
begin
  if Data_Is_Melee then
    Result := Melee.ExistPerson(odPerson)
  else
    Result := False;
end;

function TodTournament.GetFreeCourtForTournament(DoubleFlag: Boolean): TodCourt;
var
  I: Integer;
  C: TodCourt;
  te: TodTournamentEvent;
begin
  Result := nil;
  te := OwnerTournamentEvent(Self);
  for I := 0 to te.Place.CountCourts - 1 do
  begin
    C := te.Place.Court(I);
    if (C.IsFree) and (not CourtInTournament(Self, C.odId)) and ((not DoubleFlag) or (C.CourtType <> ctSingle)) then
      Exit(C);
  end;
end;

function TodTournament.TournamentTypeName: String;
begin
  Result := Data.odClass;
  if Result = 'Melee' then
    Result := 'Melee'
  else
  if Result = 'Swiss' then
    Result := 'Schweizer'
  else
  if Result = 'Group' then
    Result := 'Gruppen'
  else
  if Result = 'Ko' then
    Result := 'KO';
end;

{$REGION   'GETTER+SETTER'}
{$REGION   'data_Melee'}
procedure TodTournament.Data_Create_Melee;
begin
  Data_Free_Melee;
  FData := TodMelee.Create(Self);
  FData.OdName := 'Data';
end;

procedure TodTournament.Data_Free_Melee;
begin
  FreeAndNil(FData);
end;

function TodTournament.Data_Is_Melee: Boolean;
begin
  Result := (Assigned(FData)) and (FData is TodMelee);
end;

function TodTournament.Melee: TodMelee;
begin
  if Data_Is_Melee then
    Result := (FData as TodMelee)
  else
    Result := nil;
end;
{$ENDREGION 'data_Melee'}
{$REGION   'data_Swiss'}
procedure TodTournament.Data_Create_Swiss;
begin
  Data_Free_Swiss;
  FData := TodSwiss.Create(Self);
  FData.OdName := 'Data';
end;

procedure TodTournament.Data_Free_Swiss;
begin
  FreeAndNil(FData);
end;

function TodTournament.Data_Is_Swiss: Boolean;
begin
  Result := (Assigned(FData)) and (FData is TodSwiss);
end;

function TodTournament.Swiss: TodSwiss;
begin
  if Data_Is_Swiss then
    Result := (FData as TodSwiss)
  else
    Result := nil;
end;
{$ENDREGION 'data_Swiss'}
{$REGION   'data_Group'}
procedure TodTournament.Data_Create_Group;
begin
  Data_Free_Group;
  FData := TodGroup.Create(Self);
  FData.OdName := 'Data';
end;

procedure TodTournament.Data_Free_Group;
begin
  FreeAndNil(FData);
end;

function TodTournament.Data_Is_Group: Boolean;
begin
  Result := (Assigned(FData)) and (FData is TodGroup);
end;

function TodTournament.Group: TodGroup;
begin
  if Data_Is_Group then
    Result := (FData as TodGroup)
  else
    Result := nil;
end;
{$ENDREGION 'data_Group'}
{$REGION   'data_Ko'}
procedure TodTournament.Data_Create_Ko;
begin
  Data_Free_Ko;
  FData := TodKo.Create(Self);
  FData.OdName := 'Data';
end;

procedure TodTournament.Data_Free_Ko;
begin
  FreeAndNil(FData);
end;

function TodTournament.Data_Is_Ko: Boolean;
begin
  Result := (Assigned(FData)) and (FData is TodKo);
end;

function TodTournament.Ko: TodKo;
begin
  if Data_Is_Ko then
    Result := (FData as TodKo)
  else
    Result := nil;
end;
{$ENDREGION 'data_Ko'}
{$REGION   'data_GroupKo'}
procedure TodTournament.Data_Create_GroupKo;
begin
  Data_Free_GroupKo;
  FData := TodGroupKo.Create(Self);
  FData.OdName := 'Data';
end;

procedure TodTournament.Data_Free_GroupKo;
begin
  FreeAndNil(FData);
end;

function TodTournament.Data_Is_GroupKo: Boolean;
begin
  Result := (Assigned(FData)) and (FData is TodGroupKo);
end;

function TodTournament.GroupKo: TodGroupKo;
begin
  if Data_Is_GroupKo then
    Result := (FData as TodGroupKo)
  else
    Result := nil;
end;
{$ENDREGION 'data_GroupKo'}
{$REGION   'name'}
function TodTournament.get_Name: String;
begin
  Result := FName;
end;

procedure TodTournament.set_Name(const Value: String);
begin
  if FName <> Value then
  begin
    FName := Value;
    Changed;
  end;
end;
{$ENDREGION 'name'}
{$REGION   'activate'}
function TodTournament.get_Activate: Boolean;
begin
  Result := FActivate;
end;

procedure TodTournament.set_Activate(const Value: Boolean);
begin
  if FActivate <> Value then
  begin
    FActivate := Value;
    Changed;
  end;
end;
{$ENDREGION 'activate'}
{$REGION   'excludecourts'}
function TodTournament.get_ExcludeCourts: String;
begin
  Result := FExcludeCourts;
end;

procedure TodTournament.set_ExcludeCourts(const Value: String);
begin
  if FExcludeCourts <> Value then
  begin
    FExcludeCourts := Value;
    Changed;
  end;
end;
{$ENDREGION 'excludecourts'}
{$ENDREGION 'GETTER+SETTER'}

initialization

  RegisterClasses([TodTournament]);

end.

FredlFesl 22. Jul 2011 06:26

AW: unterschiedliche Klassen variabel instanzieren
 
Zitat:

Zitat von Bjoerk (Beitrag 1112926)
Meinst du so ?

Delphi-Quellcode:
unit Unit1;
constructor TKlasse.Create(const ATyp: integer);
begin
  if ATyp=1 then Self:= TKlasse1.Create;
  if ATyp=2 then Self:= TKlasse2.Create;
end;

Sowas würde ich nicht machen, denn die Grundklasse muss ja dann wissen, welche Ableitungen es gibt und geben wird. Die Basisklasse muss jedesmal erweitert werden, wenn eine Ableitung hinzukommt. => Verstoß gegen Grundregeln der OOP.

Sondern sowas:
Delphi-Quellcode:
Type
  TKlasseClassFactory = Class
    Class Function CreateKlasse (ATyp : Integer) : TKlasse;
  End;

Class Function TKlasseClassFactory.CreateKlasse (ATyp : Integer) : TKlasse;
Begin
  Case ATyp Of
    1 : Result := TKlasse1.Create;
    2 : Result := TKlasse2.Create;
End;
Viel besser, denn die Basisklasse wird nicht mehr angefasst. Du kannst Dir auch eine allgemeine Classfactory bauen, die in einer TClasslist (wozu gibt es die wohl?) je nach ATyp den entsprechenden Eintrag liefert.

Normalerweise ist ein 'case' Konstrukt ein indiz für schlechtes OOP, denn in einer Methode werden -je nach Wert- unterschiedliche Aktionen ausgelöst. Hier ist das jedoch erlaubt.

Ich würde übrigens aus dem 'ATYP' einen Enumerationswert machen oder zumindest die 'magic numbers' 1 und 2 als Konstante deklarieren.

Blup 22. Jul 2011 08:43

AW: unterschiedliche Klassen variabel instanzieren
 
Zitat:

- ich benötige eine Variable die die Instanz eines Objektes enthällt
Delphi-Quellcode:
var
  o: TObject;
Damit ist diese Bedingung erfüllt.
Soll das ein Varparameter oder ein Rückgabewert sein?

Zitat:

- je nach Wert eines Integer soll das entsprechende Objekt in der Variable abgelegt werden
Ist der Integer ein Parameter oder nur intern bekannt?
Wird das Object erst erzeugt oder nur ausgewählt?
Du hast nicht mal klargestellt, ob du überhaupt eine Factory bauen willst.
Delphi-Quellcode:
case i of
  1: o := TKatze.Create;
  2: o := TAuto.Create;
{...}
Zitat:

- die Objekte sind zwar von einer Basis abgeleitet haben aber unterschiedliche Properties etc.
Das ist eine Aussage die aber keinen Einfluß auf die anderen Bedingungen hat.
Man kann nur nicht direkt auf o.Scheinwerfer zugreifen, auch wenn es sich um ein Auto handelt.
Dazu muss man erst auf die richtige Klasse casten oder ein Interface abfragen.

Wenn dein Problem nicht so einfach gelöst werden kann, existieren weitere Bedingungen, die du aber nicht formuliert hast.
Die meisten scheinen hier einfach nur zu raten, was du eigentlich erreichen willst.

Bjoerk 22. Jul 2011 10:15

AW: unterschiedliche Klassen variabel instanzieren
 
Zitat:

Sowas würde ich nicht machen, denn die Grundklasse muss ja dann wissen, welche Ableitungen es gibt und geben wird. Die Basisklasse muss jedesmal erweitert werden, wenn eine Ableitung hinzukommt. => Verstoß gegen Grundregeln der OOP.
TE möchte in beiden Klassen verschiedene Methoden haben. TKlasse muß bei jedem Methodenaufruf geparst werden (with Klasse as TKlasse1 do ..) das ist alles. Aber ich stimme dir grundsätzlich zu, nicht sehr schön..

FredlFesl 22. Jul 2011 10:59

AW: unterschiedliche Klassen variabel instanzieren
 
Zitat:

Zitat von Bjoerk (Beitrag 1112999)
TE möchte in beiden Klassen verschiedene Methoden haben.

Das ist manchmal so bei Ableitungen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:20 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz