Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Klassenstruktur / Basisklasse -> Kindklassen => gleiche Datenbasis (https://www.delphipraxis.net/169513-klassenstruktur-basisklasse-kindklassen-%3D-gleiche-datenbasis.html)

MarioM. 25. Jul 2012 14:39

Delphi-Version: 2010

Klassenstruktur / Basisklasse -> Kindklassen => gleiche Datenbasis
 
Hallo zusammen,

ich stehe vor folgendem Problem.

Ich möchte gerne aus Daten, die ich in Objekten sammle, eine XML-Struktur aufbauen, mit der Aufgaben übermittelt werden; das aber nur als Hintergrund.

Das Ganze soll so aussehen, dass es verschiedene Aufgaben gibt, für die bestimmte Tags im XML gefüllt werden sollen.

Um den Anwender der Klassen möglichst gut zu führen, habe ich das Ganze so aufgebaut, dass ich eine Basisklasse habe, die alle möglichen Felder enthält, die in den Aufgaben vorkommen können.
Zusätzlich habe ich für jede mögliche Aufgabe eine Klasse von dieser Basisklasse abgeleitet, und nur die Eigenschaften public gesetzt, die für die Aufgabe relevant sind.
So sieht der Anwender genau, wenn er eine Instanz einer Aufgaben-Klasse erzeugt, welche Informationen benötigt werden.

Jetzt nehmen wir mal an, die Information NAME ist sowohl für Aufgabe1 als auch Aufgabe2 relevant. Ich möchte aber, dass Aufgabe1.NAME überschrieben wird, wenn Aufgabe2.NAME gesetzt wird - alle Aufgabensollen also auf die gleichen Daten zugreifen, und nicht jede Aktion einen eigenen Datenbereich haben.

In der Basisklasse sind alle Felder private, die entsprechenden Properties dazu protected. Was in der Ableitung zugänglich sein soll, habe ich dort public gemacht.

wie lässt sich das realisieren?

Panthrax 25. Jul 2012 16:39

AW: Klassenstruktur / Basisklasse -> Kindklassen => gleiche Datenbasis
 
Liste der Anhänge anzeigen (Anzahl: 1)
Den Teil mit dem XML habe ich nicht verstanden. Aber es klingt danach als müsste man dafür die Klasse TDaten in meinem Vorschlag ändern. Für alles andere: So vielleicht? -- Vorschlag im Anhang.

Namenloser 26. Jul 2012 04:02

AW: Klassenstruktur / Basisklasse -> Kindklassen => gleiche Datenbasis
 
Verstehe ich das richtig, dass alle Klassen auf die gleiche Datenstruktur zugreifen und du willst, dass alle Änderungen synchron sind?

In dem Fall solltest du deine Klassenstruktur anders aufbauen – das, was aktuell deine Basisklasse ist, sollte eine unabhängige Dokument-Klasse sein. [edit]Oder du verwendest Interfaces, siehe unten.[/edit] Deine Aufgaben-Klassen können sich dann mit einer Instanz der Dokument-Klasse verbinden und auf deren Datenbestand zugreifen.

Beispiel:
Delphi-Quellcode:
TDocument = class
public
  function GetProperty(Identifier: string): Variant;
  procedure SetProperty(Identifier: string; Value: Variant);
end;

TTaskBase = class
protected
  FDocument: TDocument;
public
  constructor Create(Document: TDocument);
end;

TTaskAlpha = class(TTaskBase)
protected
  function GetPropertyA: Integer;
  procedure SetPropertyA(Value: Integer);
  function GetPropertyB: String;
  procedure SetPropertyB(Value: String);
public
  property PropertyA: Integer read GetPropertyA write SetPropertyA;
  property PropertyB: String read GetPropertyB write SetPropertyB;
end;

TTaskBeta = class(TTaskBase)
protected
  function GetPropertyA: Integer;
  procedure SetPropertyA(Value: Integer);
  function GetPropertyC: Boolean;
  procedure SetPropertyC(Value: Boolean);
public
  property PropertyA: Integer read GetPropertyA write SetPropertyA;
  property PropertyC: Boolean read GetPropertyC write SetPropertyC;
end;

implementation

constructor TTaskBase.Create(Document: TDocument);
begin
  inherited Create;
  FDocument := Document;
end;

function TTaskAlpha.GetPropertyA: Integer;
begin
  Result := FDocument.GetProperty('A');
end;
procedure TTaskAlpha.SetPropertyA(Value: Integer);
begin
  FDocument.SetProperty('A', Value);
end;

function TTaskAlpha.GetPropertyB: String;
begin
  Result := FDocument.GetProperty('B');
end;
procedure TTaskAlpha.SetPropertyB(Value: String);
begin
  FDocument.SetProperty('B', Value);
end;

function TTaskBeta.GetPropertyA: Integer;
begin
  Result := FDocument.GetProperty('A');
end;
procedure TTaskBeta.SetPropertyA(Value: Integer);
begin
  FDocument.SetProperty('A', Value);
end;

function TTaskBeta.GetPropertyC: Boolean;
begin
  Result := FDocument.GetProperty('C');
end;
procedure TTaskBeta.SetPropertyC(Value: Boolean);
begin
  FDocument.SetProperty('C', Value);
end;
Delphi-Quellcode:
var
  Document: TDocument;
  TaskAlpha: TTaskAlpha;
  TaskBeta: TTaskBeta;
begin
  Document := TDocument.Create(...);
  // * Document irgendwie initialisiert *

  TaskAlpha := TTaskAlpha.Create(Document);
  TaskBeta := TTaskBeta.Create(Document);

  // Property A ändert ihren Wert synchron:
  TaskAlpha.PropertyA := 42;
  writeln(TaskAlpha.PropertyA); // gibt 42 aus
  writeln(TaskBeta.PropertyA); // gibt 42 aus

  TaskBeta.PropertyA := 314;
  writeln(TaskAlpha.PropertyA); // gibt 314 aus
  writeln(TaskBeta.PropertyA); // gibt 314 aus

  // Diese Properties werden im Beispiel nur von jeweils einer Aufgabenklasse benutzt:
  TaskAlpha.PropertyB := 'Foo';
  TaskBeta.PropertyC := True;

Edit: Eine andere Variante wäre, mit Interfaces zu arbeiten – das entspräche eher deinem bisherigen Ansatz. Aber das geht natürlich nur, wenn deine Aufgabenklassen kein neues Verhalten (z.B. neue Methoden) einführen, sondern ausschließlich Untermengen der Mutterklasse bereitstellen (wobei die Klasse tendenziell zum God-Object mutieren könnte):
Delphi-Quellcode:
  ITaskAlpha = interface
    function GetPropertyA: Integer;
    procedure SetPropertyA(Value: Integer);
    function GetPropertyB: String;
    procedure SetPropertyB(Value: String);

    property PropertyA: integer read GetPropertyA write SetPropertyA;
    property PropertyB: string read GetPropertyB write SetPropertyB;
  end;

  ITaskBeta = interface
    function GetPropertyA: Integer;
    procedure SetPropertyA(Value: Integer);
    function GetPropertyC: Boolean;
    procedure SetPropertyC(Value: Boolean);

    property PropertyA: integer read GetPropertyA write SetPropertyA;
    property PropertyC: boolean read GetPropertyC write SetPropertyC;
  end;

  TDocument = class(TInterfacedObject, ITaskAlpha, ITaskBeta)
    function GetPropertyA: Integer;
    procedure SetPropertyA(Value: Integer);
    function GetPropertyB: String;
    procedure SetPropertyB(Value: String);
    function GetPropertyC: Boolean;
    procedure SetPropertyC(Value: Boolean);
  end;
Und dann so:
Delphi-Quellcode:
var
  Document: TDocument;
  TaskAlpha: ITaskAlpha;
  TaskBeta: ITaskBeta;
begin
  Document := TDocument.Create(...);
  // * Document irgendwie initialisiert *

  TaskAlpha := Document as ITaskAlpha;
  TaskBeta := Document as ITaskBeta;

  // Rest wie oben
Interfaces und Objekte auf diese Art zu mischen kann übrigens auch wegen der Referenzzählung bei Interfaces nach hinten losgehen, wenn man nicht aufpasst.

MarioM. 26. Jul 2012 07:49

AW: Klassenstruktur / Basisklasse -> Kindklassen => gleiche Datenbasis
 
Hallo, und danke für Eure ausführlichen Beispiele.

Ich habe das Ganze nun so implementiert; und es funktioniert :)

Delphi-Quellcode:
Unit Unit1;

Interface

Uses
   SysUtils;

Type

   TTestArray = Array Of String;

   TDatenklasse = Class
   private
      FWert1: String;
      FWert2: String;
      FWert3: Integer;
      FWert4: TTestArray;
   End;

   TAufgabeBasis = Class
   private
      FDatenklasse: TDatenklasse;

      Function GetWert1: String;
      Function GetWert2: String;
      Function GetWert3: Integer;
      Procedure SetWert1(Const Value: String);
      Procedure SetWert2(Const Value: String);
      Procedure SetWert3(Const Value: Integer);
      Function GertWert4: TTestArray;
   protected
      Property Wert1: String read GetWert1 write SetWert1;
      Property Wert2: String read GetWert2 write SetWert2;
      Property Wert3: Integer read GetWert3 write SetWert3;
      Property Wert4: TTestArray read GertWert4;

      Procedure AddToWert4(Value: String); virtual;
   public
      Constructor Create;
   End;

   TAufgabe1 = Class(TAufgabeBasis)
   public
      Property Wert1;
      Property Wert2;

      Procedure AddToWert4(Value: String); override;
   End;

   TAufgabe2 = Class(TAufgabeBasis)
   public
      Property Wert2;
      Property Wert3;

      Procedure AddToWert4(Value: String); override;
   End;

Var
   GDatenklasse: TDatenklasse;

Implementation

{ TAufgabeBasis }

// ********************************************************************

Procedure TAufgabeBasis.AddToWert4(Value: String);
Begin
   SetLength(FDatenklasse.FWert4, Length(FDatenklasse.FWert4) + 1);
   FDatenklasse.FWert4[Length(FDatenklasse.FWert4) - 1] := Value;
End;

// ********************************************************************

Constructor TAufgabeBasis.Create;
Begin
   FDatenklasse := GDatenklasse;
End;

// ********************************************************************

Function TAufgabeBasis.GertWert4: TTestArray;
Begin
   Result := FDatenklasse.FWert4;
End;

// ********************************************************************

Function TAufgabeBasis.GetWert1: String;
Begin
   Result := FDatenklasse.FWert1;
End;

// ********************************************************************

Function TAufgabeBasis.GetWert2: String;
Begin
   Result := FDatenklasse.FWert2;
End;

// ********************************************************************

Function TAufgabeBasis.GetWert3: Integer;
Begin
   Result := FDatenklasse.FWert3;
End;

// ********************************************************************

Procedure TAufgabeBasis.SetWert1(Const Value: String);
Begin
   FDatenklasse.FWert1 := Value;
End;

// ********************************************************************

Procedure TAufgabeBasis.SetWert2(Const Value: String);
Begin
   FDatenklasse.FWert2 := Value;
End;

// ********************************************************************

Procedure TAufgabeBasis.SetWert3(Const Value: Integer);
Begin
   FDatenklasse.FWert3 := Value;
End;

// ********************************************************************

{ TAufgabe2 }

Procedure TAufgabe2.AddToWert4(Value: String);
Begin
   Inherited;
End;

// ********************************************************************

{ TAufgabe1 }

Procedure TAufgabe1.AddToWert4(Value: String);
Begin
   Inherited;
End;

// ********************************************************************

Initialization
   GDatenklasse := TDatenklasse.Create;

Finalization
   FreeAndNil(GDatenklasse);

End.
Delphi-Quellcode:
Unit Unit2;

Interface

Uses
   Windows,
   Messages,
   SysUtils,
   Variants,
   Classes,
   Graphics,
   Controls,
   Forms,
   Dialogs,
   Unit1;

Type
   TForm2 = Class(TForm)
      Procedure FormCreate(Sender: TObject);
   private
      FAufgabe1: TAufgabe1;
      FAufgabe2: TAufgabe2;
   End;

Var
   Form2: TForm2;

Implementation

{$R *.dfm}

Procedure TForm2.FormCreate(Sender: TObject);
Begin
   FAufgabe1 := TAufgabe1.Create;
   FAufgabe2 := TAufgabe2.Create;

   FAufgabe1.Wert1 := 'Wert1 aus Aufgabe 1';
   FAufgabe1.Wert2 := 'Wert2 aus Aufgabe 1';
   FAufgabe1.AddToWert4('Wert 4 aus Aufgabe 1');

   FAufgabe2.Wert2 := 'Wert2 aus Aufgabe 2';
   FAufgabe2.Wert3 := 3;
   FAufgabe2.AddToWert4('Wert 4 aus Aufgabe 2');
End;

// ********************************************************************

End.

omata 26. Jul 2012 09:47

AW: Klassenstruktur / Basisklasse -> Kindklassen => gleiche Datenbasis
 
Zitat:

Zitat von MarioM. (Beitrag 1175950)
Delphi-Quellcode:
Var
   GDatenklasse: TDatenklasse;

Implementation

{ TAufgabeBasis }

Jetzt müsste nur noch die Projekt-Globale-Variable verschwinden...

Delphi-Quellcode:

Implementation

Var
   GDatenklasse: TDatenklasse;

{ TAufgabeBasis }

Sir Rufo 26. Jul 2012 10:09

AW: Klassenstruktur / Basisklasse -> Kindklassen => gleiche Datenbasis
 
Wofür wird diese globale Variable überhaupt benötigt? :gruebel:

mE ist die total unsinnig

omata 26. Jul 2012 10:14

AW: Klassenstruktur / Basisklasse -> Kindklassen => gleiche Datenbasis
 
Zitat:

Zitat von Sir Rufo (Beitrag 1175958)
Wofür wird diese globale Variable überhaupt benötigt? :gruebel:

mE ist die total unsinnig

Für die gemeinsame Datenbasis. Schau dir mal den Konstruktur an.

MarioM. 26. Jul 2012 10:50

AW: Klassenstruktur / Basisklasse -> Kindklassen => gleiche Datenbasis
 
Zitat:

Zitat von omata (Beitrag 1175956)
Jetzt müsste nur noch die Projekt-Globale-Variable verschwinden...

Upsi - danke für den Hinweis :)

Sir Rufo 26. Jul 2012 12:04

AW: Klassenstruktur / Basisklasse -> Kindklassen => gleiche Datenbasis
 
Zitat:

Zitat von omata (Beitrag 1175959)
Zitat:

Zitat von Sir Rufo (Beitrag 1175958)
Wofür wird diese globale Variable überhaupt benötigt? :gruebel:

mE ist die total unsinnig

Für die gemeinsame Datenbasis. Schau dir mal den Konstruktur an.

Jetzt wo du es sagst ... hmmm also ein heimliches Singleton durch die Hintertür ... ok, wenn es so läuft :)


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:26 Uhr.

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