Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Hilfklasse (https://www.delphipraxis.net/210672-hilfklasse.html)

freimatz 25. Mai 2022 07:32

Hilfklasse
 
Hoi, Sorry Betreff ist mir kein besserer eingefallen.
Es geht um eine Monsterklasse zu vereinfachen. Zum Beispiel (real ist anders)
Delphi-Quellcode:
IMonster = interface
property ElementTyp: TTyp; //Kreis, Quadrat, etc.
property ElementFarbe: TColor;
property ElementGroesse: Double;
...
property ReferenzTyp: TTyp;
property ReferenzFarbe: TColor;
property ReferenzGroesse: Double;
...
Die implementiere Klasse hat da Getter die auf interne Daten zugreifen. Nun hätte ich das gerne so:
Delphi-Quellcode:
IElement=interface
property Typ: TTyp; //Kreis, Quadrat, etc.
property Farbe: TColor;
property Groesse: Double;
...
IMonster = interface
property Element: IElement;
property Referenz: IElement;
Damit könnte ich mir etliche Properties sparen.
Statt Monster.ReferenzFarbe schreibt man dann halt Monster.Referenz.Farbe.
Aber wie implementiert man das dann? Klar: TElement = class(TInterfacedObject, IElement). Aber dann muss diese Klasse ja noch TMonster kennen. Oder es bekommt die Daten bei der Erzeugung mit. Dann muss die Klasse aber jedesmal neu erzeugt werden oder es braucht eine Benachrichtigung wenn sich die Daten ändern.
Kann ich alles machen aber scheint mir recht aufwendig. (Nein - Datenhaltung in TElement kommt nicht in Frage)
:gruebel:

TiGü 25. Mai 2022 08:46

AW: Hilfklasse
 
Delphi-Quellcode:
constructor TMonster.Create;
begin
...
  FElement := TElement.Create(Self); //Übergabeparameter nach Lust und Laune benennen: Parent, Owner, Lieblingsonkel, Datengrab oder Mutti
  FReferenz := TElement.Create(Self);
...
end;

Der schöne Günther 25. Mai 2022 08:46

AW: Hilfklasse
 
Ich tue mich schwer das Beispiel zu verstehen. Element sind doch einfach nur Eigenschaften (Daten), keine Objekt, das etwas tut. Ich verstehe auch nicht, weshalb es TMonster/IMonster überhaupt kennen muss. Machen tut doch das Monster. Es guckt auf sein Feld
Delphi-Quellcode:
Referenz
oder
Delphi-Quellcode:
Element
und handelt entsprechend.

Geht es dir darum, in Monster direkt darauf zu reagieren, wenn man im Code z.B. sagt
Delphi-Quellcode:
meinMonster.Referenz.Farbe := blau
? Dann würde man der Referenz im Konstruktor wohl einen Callback mitgeben den es aufrufen soll, wenn auf ihm was geändert wird. Oder es hat eine Event-Variable
Delphi-Quellcode:
OnChange
an die sich das Monster binden kann, um darauf zu reagieren dass in Referenz sich etwas geändert hat.

Ein "Real World Example" ist wohl z.B.
Delphi-Quellcode:
meinLabel.Font.Size := xx
. Da ist das auch so gelöst.

Klingt kompliziert? Ist es auch. Ich finde das Overkill. Und das hängt mit diesem Property-Gefummel zusammen, gegen das ich nicht müde werde zu wettern, aber damit stehe ich hier im Delphi-Forum eher alleine da.

Wie würde ich es machen?
  • Element ist ein Record mit Feldern "Größe", "Farbe" usw.
  • Element bekommt noch eine Operator-Überladung sodass ich z.B. sagen kann
    Delphi-Quellcode:
    if elementNeu = elementAlt then ...
  • Ich gebe IMonster nur ein getElement(): TElement und setElement(const element: TElement)
  • In setElement(..) wird geprüft ob sich element überhaupt verändert hat, und wenn ja, dann tu etwas (z.B. zeichne dich neu)

Ja, statt
Delphi-Quellcode:
meinMonster.Referenz.Farbe := blau
tippe ich jetzt
Delphi-Quellcode:
referenz := meinMonster.getReferenz();
referenz.farbe := blau;
meinMonster.setReferenz(referenz);
Hebt sich aber auch schon wieder auf, wenn ich z.B. nicht nur Farbe, sondern auch Größe anpassen will. Ja, es sind ein paar Buchstaben mehr zu tippen wenn man was ändert. Aber an den anderen Stellen wird es so viel simpler: Die Daten sind an einem Platz, und sind wirklich nur Daten, und ich brauche keine Callbacks für Änderungen "eins weiter unten".

freimatz 25. Mai 2022 11:52

AW: Hilfklasse
 
Danke für alle Rückmeldungen und Anregungen.

@TiGü: Sowas habe ich befürchtet. Statt einem einfachen Zugriff muss ich dann im Getter auf den Parent, Owner, Monster ... zugreifen. Wird dann beim Debuggen etwas mühsamer. Aber vermutlich noch die beste Lösung.

@Günther: Ich schrieb bereits: "Nein - Datenhaltung in TElement kommt nicht in Frage". Die Daten sind in TMonster und dort sogar nur über virtuelle Getter verfügbar. Wenn dann das TElement die Daten liefern soll, dann muss es auf TMonster zugreifen oder von dort die Daten bekommen. Das entspräche auch dem von Dir erwähnten Callback/Event/OnChange. Im Übrigen sind es wohl nur Lesezugriffe.

himitsu 25. Mai 2022 14:08

AW: Hilfklasse
 
Den Owner braucht man nur, wenn die ParentComponent die SubKomponenten automatisch freigeben soll (ohne manuelles Free),
oder wenn die SubKomponenten auf die übergeordnete Komponente zugreifen müssen. (z.B. bei Zuweiseung Daten dort ändern oder Notifications/OnChangeEvents dort auslösen)

Die Freigabe kann/wird hier über die Referenzzählung des Interfaces erledigt. (FElement ist ja ein IElement und kein TElement)

Blup 25. Mai 2022 15:30

AW: Hilfklasse
 
Wenn die Struktur des IMonster-Interface und die zugrunde liegenden Klassen nicht verändert werden können, bau eine Fassade, die deinem Anwendungsfall gerecht wird.
Delphi-Quellcode:
unit Fassade;

interface

uses
  Graphics;

type
  TTyp = (Kreis, Quadrat);

  IMonster = interface(IInterface)
    function GetElementTyp: TTyp;
    function GetElementFarbe: TColor;
    function GetElementGroesse: Double;
    function GetReferenzTyp: TTyp;
    function GetReferenzFarbe: TColor;
    function GetReferenzGroesse: Double;
    {...}
    property ElementTyp: TTyp read GetElementTyp;
    property ElementFarbe: TColor read GetElementFarbe;
    property ElementGroesse: Double read GetElementGroesse;
    {...}
    property ReferenzTyp: TTyp read GetReferenzTyp;
    property ReferenzFarbe: TColor read GetReferenzFarbe;
    property ReferenzGroesse: Double read GetReferenzGroesse;
    {...}
  end;

  IElement = interface(IInterface)
    function GetTyp: TTyp;
    function GetFarbe: TColor;
    function GetGroesse: Double;
    {...}
    property Typ: TTyp read GetTyp;
    property Farbe: TColor read GetFarbe;
    property Groesse: Double read GetGroesse;
  end;

  IMonsterFassade = interface(IInterface)
    function GetElement: IElement;
    function GetReferenz: IElement;
    {...}
    property Element: IElement read GetElement;
    property Referenz: IElement read GetReferenz;
  end;

  TMonsterReferenz = class(TInterfacedObject)
    constructor Create(AParent: IMonster);
    destructor Destroy; override;
  protected
    FParent: IMonster;
  end;

  TElement = class(TMonsterReferenz, IElement)
    function GetTyp: TTyp;
    function GetFarbe: TColor;
    function GetGroesse: Double;
  end;

  TReferenz = class(TMonsterReferenz, IElement)
    function GetTyp: TTyp;
    function GetFarbe: TColor;
    function GetGroesse: Double;
  end;

  TMonsterFassade = class(TMonsterReferenz, IMonsterFassade)
    constructor Create(AParent: IMonster);
    destructor Destroy; override;
  private
    FElement: IElement;
    FReferenz: IElement;
  public
    function GetElement: IElement;
    function GetReferenz: IElement;
  end;

implementation


{ TMonsterReferenz }

constructor TMonsterReferenz.Create(AParent: IMonster);
begin
  inherited;
  FParent := IMonster;
end;

destructor TMonsterReferenz.Destroy;
begin
  FParent := nil;
  inherited;
end;

{ TElement }

function TElement.GetFarbe: TColor;
begin
  Result := FParent.GetElementFarbe
end;

function TElement.GetGroesse: Double;
begin
  Result := FParent.GetElementGroesse;
end;

function TElement.GetTyp: TTyp;
begin
  Result := FParent.GetElementTyp;
end;

{ TReferenz }

function TReferenz.GetFarbe: TColor;
begin
  Result := FParent.GetReferenzFarbe
end;

function TReferenz.GetGroesse: Double;
begin
  Result := FParent.GetReferenzGroesse;
end;

function TReferenz.GetTyp: TTyp;
begin
  Restlt := FParent.GetReferenzTyp;
end;

{ TMonsterFassade }

constructor TMonsterFassade.Create(AParent: IMonster);
begin
  inherited;
  FElement := TElement.Create(AParent);
  FReferenz := TReferenz.Create(AParent);
end;

destructor TMonsterFassade.Destroy;
begin
  FElement := nil,
  FReferenz := nil,
  inherited;
end;

function TMonsterFassade.GetElement: IElement;
begin
  Result := FElement;
end;

function TMonsterFassade.GetReferenz: IElement;
begin
  Result := FReferenz;
end;

end.

Uwe Raabe 25. Mai 2022 17:04

AW: Hilfklasse
 
Wenn die Getter schon passend in TMonster vorhanden sind, dann kannst du es ja auch mal so versuchen:
Delphi-Quellcode:
type
  TTyp = (Kreis, Quadrat);

type
  IElement = interface
    function GetFarbe: TColor;
    function GetGroesse: Double;
    function GetTyp: TTyp;
    property Typ: TTyp read GetTyp;
    property Farbe: TColor read GetFarbe;
    property Groesse: Double read GetGroesse;
  end;

  IReferenz = interface(IElement)
  end;

  IMonster = interface
    function GetElement: IElement;
    function GetReferenz: IReferenz;
    property Element: IElement read GetElement;
    property Referenz: IReferenz read GetReferenz;
  end;

type
  TMonster = class(TInterfacedObject, IMonster, IElement, IReferenz)
    function IElement.GetFarbe = GetElementFarbe;
    function IElement.GetGroesse = GetElementGroesse;
    function IElement.GetTyp = GetElementTyp;
    function IReferenz.GetFarbe = GetReferenzFarbe;
    function IReferenz.GetGroesse = GetReferenzGroesse;
    function IReferenz.GetTyp = GetReferenzTyp;
  private
    function GetElement: IElement;
    function GetReferenz: IReferenz;
    function GetElementFarbe: TColor;
    function GetElementGroesse: Double;
    function GetElementTyp: TTyp;
    function GetReferenzFarbe: TColor;
    function GetReferenzGroesse: Double;
    function GetReferenzTyp: TTyp;
  end;

{ TMonster }

function TMonster.GetElement: IElement;
begin
  Result := Self as IElement;
end;

function TMonster.GetReferenz: IReferenz;
begin
  Result := Self as IReferenz;
end;

freimatz 27. Mai 2022 09:42

AW: Hilfklasse
 
Hallo zusammen,
Danke nochmals für alle Vorschläge. Klar ist auf jeden Fall dass "nimm das Brett vor Deinem Kopf und machs einfach so" nicht geht. :cry: :-D

Ich mach mich mal ran und versuche was so ähnlich wie Eure Vorschläge. Das IMonster-Interface und die zugrunde liegenden Klassen kann (und will) ich schon ändern, halt nur nicht so, dss die Daten nicht mehr von dort kommen. Vielleicht kann ich auf den Wrapper verzichten.

Passieren muss auf jeden Fall was. :feuerchen: IMonster hat 95 properties, dazu noch viele Einzel-Methoden und noch Nachfahren die noch vieles zu nehmen.

Uwe Raabe 30. Mai 2022 13:52

AW: Hilfklasse
 
Ich habe meinen Vorschlag nochmal überarbeitet. Das IReferenz Interface wird eigentlich nur für die Method Resolution Clause und in GetReferenz verwendet und muss somit gar nicht öffentlich sein.

Mit diesem Ansatz muss also nur die Implementierung entsprechend formuliert werden. Zusätzliche Hilfsklassen sind nicht notwendig.
Delphi-Quellcode:
type
  TTyp = (Kreis, Quadrat);

type
  IElement = interface
    function GetFarbe: TColor;
    function GetGroesse: Double;
    function GetTyp: TTyp;
    property Typ: TTyp read GetTyp;
    property Farbe: TColor read GetFarbe;
    property Groesse: Double read GetGroesse;
  end;

  IMonster = interface
    function GetElement: IElement;
    function GetReferenz: IElement;
    property Element: IElement read GetElement;
    property Referenz: IElement read GetReferenz;
  end;
Delphi-Quellcode:
type
  IReferenz = interface(IElement)
  end;
 
TMonster = class(TInterfacedObject, IMonster, IElement, IReferenz)
    function IElement.GetFarbe = GetElementFarbe;
    function IElement.GetGroesse = GetElementGroesse;
    function IElement.GetTyp = GetElementTyp;
    function IReferenz.GetFarbe = GetReferenzFarbe;
    function IReferenz.GetGroesse = GetReferenzGroesse;
    function IReferenz.GetTyp = GetReferenzTyp;
  private
    function GetElement: IElement;
    function GetReferenz: IElement;
    function GetElementFarbe: TColor;
    function GetElementGroesse: Double;
    function GetElementTyp: TTyp;
    function GetReferenzFarbe: TColor;
    function GetReferenzGroesse: Double;
    function GetReferenzTyp: TTyp;
  end;

{ TMonster }

function TMonster.GetElement: IElement;
begin
  Result := Self as IElement;
end;

function TMonster.GetReferenz: IElement;
begin
  Result := Self as IReferenz;
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:16 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