|
Antwort |
Registriert seit: 23. Dez 2007 Ort: Schwarzwald 505 Beiträge Delphi 12 Athens |
#1
Hallo zusammen,
ich bin dabei mich verstärkt in die Verwendung von Klassen einzuarbeiten und stoße immer wieder auf Unsicherheiten. Ich habe mal folgenden Klassenaufbau implementiert:
Delphi-Quellcode:
Dieser Aufbau ist stark vereinfacht, weshalb man nicht zuviel Zeit in Sinn oder Unsinn stecken sollte. In der Realklasse sind auch private/public-Bereiche und Properties verwendet. Das wichtige ist mir die Struktur der Klassen mit Unterklassen, und die THardwareList, die unterschiedliche Objekte aufnehmen kann. Jedes Objekt kann dabei nur einmal vorkommen. Es müssen aber nicht alle möglichen Objekte in der Liste vorhanden sein. Wie im Beispiel oben, wo nur HardwarePower in der Liste enthalten ist.
unit Unit5;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Generics.Collections, Vcl.StdCtrls; type // Aufbau der Klassenstruktur // ...TTest // ....└─ TTestplan // ........└─ THardware // ............└─ THardwareList (TObjectList<THardwareBase>) // ................├─ THardwarePower (THardwareBase) // ................|...├─ TConfigPower (TConfigBase) // ................|...└─ TProcPower // ................└─ THardwareSpeed (THardwareBase) // ....................├─ TConfigSpeed (TConfigBase) // ....................└─ TProcSpeed TForm1 = class(TForm) btReadPower: TButton; Label1: TLabel; btCreateTest: TButton; btCreatePower: TButton; procedure btReadPowerClick(Sender: TObject); procedure btCreateTestClick(Sender: TObject); procedure btCreatePowerClick(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; type TConfigBase = class ID : Integer; Name : String; Port : String; constructor Create; destructor Destroy; override; end; TConfigPower = class(TConfigBase) PowerRange : String; end; TConfigSpeed = class(TConfigBase) SpeedRange : String; end; TProcPower = class function GetPower:Real; constructor Create; destructor Destroy; override; end; TProcSpeed = class function GetSpeed: Real; constructor Create; destructor Destroy; override; end; THardwareBase = class Name : String; constructor Create; destructor Destroy; override; end; THardwarePower = class(THardwareBase) Config : TConfigPower; Proc : TProcPower; constructor Create; destructor Destroy; override; end; THardwareSpeed = class(THardwareBase) Config : TConfigSpeed; Proc : TProcSpeed; constructor Create; destructor Destroy; override; end; THardwareList = class(TObjectList<THardwareBase>) public function GetHardwarePower: THardwarePower; function GetHardwareSpeed: THardwareSpeed; end; THardware = class Name : String; HardwareList : THardwareList; constructor Create; destructor Destroy; override; end; TTestplan = class Name :String; Hardware : THardware; constructor Create; destructor Destroy; override; end; TTest = class Name :String; Testplan : TTestplan; constructor Create; destructor Destroy; override; end; var Form1: TForm1; Test : TTest; implementation {$R *.dfm} { TConfigBase } constructor TConfigBase.Create; begin end; destructor TConfigBase.Destroy; begin inherited; end; { TProcPower } constructor TProcPower.Create; begin end; destructor TProcPower.Destroy; begin inherited; end; function TProcPower.GetPower: Real; begin end; { TProcSpeed } constructor TProcSpeed.Create; begin end; destructor TProcSpeed.Destroy; begin inherited; end; function TProcSpeed.GetSpeed: Real; begin end; { THardwareBase } constructor THardwareBase.Create; begin end; destructor THardwareBase.Destroy; begin inherited; end; { THardwarePower } constructor THardwarePower.Create; begin Config := TConfigPower.Create; Proc := TProcPower.Create; end; destructor THardwarePower.Destroy; begin Config.Free; Proc.Free; inherited; end; { THardwareSpeed } constructor THardwareSpeed.Create; begin Config := TConfigSpeed.Create; Proc := TProcSpeed.Create; end; destructor THardwareSpeed.Destroy; begin Config.Free; Proc.Free; inherited; end; { THardwareList } function THardwareList.GetHardwarePower: THardwarePower; var lObject: TObject; begin Result := nil; for lObject in self do if lObject is THardwarePower then begin Result := lObject as THardwarePower; break; end; end; function THardwareList.GetHardwareSpeed: THardwareSpeed; begin end; { THardware } constructor THardware.Create; begin HardwareList := THardwareList.Create(true); end; destructor THardware.Destroy; begin HardwareList.Free; inherited; end; { TTestplan } constructor TTestplan.Create; begin end; destructor TTestplan.Destroy; begin Hardware.Free; inherited; end; { TTest } constructor TTest.Create; begin end; destructor TTest.Destroy; begin Testplan.Free; inherited; end; procedure TForm1.btCreateTestClick(Sender: TObject); begin if not assigned(Test) then begin Test := TTest.Create; Test.Testplan := TTestplan.Create; Test.Testplan.Hardware := THardware.Create; end; end; procedure TForm1.btReadPowerClick(Sender: TObject); begin if assigned(Test) and assigned(Test.Testplan) and assigned(Test.Testplan.Hardware) and assigned(Test.Testplan.Hardware.HardwareList) and (Test.Testplan.Hardware.HardwareList.GetHardwarePower <> nil) then Label1.Caption := Test.Testplan.Hardware.HardwareList.GetHardwarePower.Name else Label1.Caption := 'Keine Leistungsmessgerät gefunden'; end; procedure TForm1.btCreatePowerClick(Sender: TObject); var HardwarePower : THardwarePower; begin HardwarePower := THardwarePower.Create; HardwarePower.Name := 'Fluke 300'; Test.Testplan.Hardware.HardwareList.Add(HardwarePower); end; end. Meine Frage sind:
Vielen Dank für eure Unterstützung Gerd Geändert von norwegen60 ( 4. Nov 2017 um 11:11 Uhr) |
Zitat |
Registriert seit: 6. Feb 2008 838 Beiträge |
#2
...Welche Unterschiede ergeben sich, ob ich THardwareList = class(TObjectList<THardwareBase>) oder THardwareList = class(TObjectList<TObject>) definiere?"...
Über die Elemente eine reinen TObjectList kannst du nicht ohne Cast weiter einfach nach innen per ".???" zugreifen. Trotzdem würd ich als keine Vereinfachung in deinem Kontept die separate Listenklasse sparen und die Liste direkt in THardware aanlegen, also so:
Delphi-Quellcode:
so tiefer Direktzugriff ist nie gut... und hier auch noch logisch falsch weil du kein Element von deiner "Hardwarelist" ausgewält hast!?
THardware = class
Name : String; HardwareList : TObjectList<THardwareBase>; constructor Create; destructor Destroy; override; property Count:Integer read GetHardwareListCount; property Value[idx:Integer]:THardwareBase read GetHardwareListValue; default; end; statt "Test.Testplan.Hardware.HardwareList[?].GetHardwarePower.Name" besser so allgemeines per Property in Hardware sicher lösen, wo es zur Vorabfrage auch einen Count gibt. also "if Test.Testplan.Hardware.HardwareList.Count>0 then Caption:=Test.Testplan.Hardware.HardwareList[0].Name" oder besser wären CountProperty und ein virtuelles ArrayProperty(als "default") in THardware, so dass dann dies so funktioniert ala "Test.Testplan.Hardware.Count" "if Test.Testplan.Hardware.Count>0 then Caption:=Test.Testplan.Hardware[0].Name" Rein logisch sehe ich mit aktuellen Angaben auch noch keinen Grund für Test->Testplan.. ausser es kann/soll mal innerhalb eines Tests mehrere Testpläne geben? Ich würde aber eher in Richtung Testplan->Tests denken, also das ein Testplan mehrere Tests beinhalten kann, dann würde ähnlich "THardware" TTestplan eine TObjecvtlist<TTest> bekommen und auch wieder eine Count und eine Arrayproperty... "if (Testplan.Count>0) and (Testplan[0].Hardware.Count>0) then Caption:=Testplan[0].Hardware[0].Name" ...das sähe dann für mich schon viele schöner und praktischer aus und du sparst dir die Assigned-Kette weil es ist immer bis zur Liste alles vorhanden(legt man im "contruktor" an und gibt es im "destruktor" frei), nur ob Elemente in der Liste ist per ".count" zu prüfen wenn direkt auf ein Element zugegriffen werden soll. ...bei Zugriffen auf undefinierte Elemente kannst du so selbst entscheiden kontrolliert eine Exeption zu werfen, oder per eigenem Defaultelement für undefined/outofrange eine gültig Standardrückgabe erzeugen. Geändert von mensch72 ( 4. Nov 2017 um 15:48 Uhr) |
Zitat |
Registriert seit: 23. Dez 2007 Ort: Schwarzwald 505 Beiträge Delphi 12 Athens |
#3
Hallo mensch72,
zuerst muss ich vielleicht dazu sagen, dass manche Dinge in dem Projekt, an dem ich mitarbeite, schon gegegen sind. Dazu gehört die Struktur bis runter zu THardware. Im Moment ist dann aber alles was man Hardwareinformationen braucht, in eine riesiges, flaches THardware-Element gepackt. Jetzt wollten wir eigene Elemente für jedes Messgerät erstellen. so tiefer Direktzugriff ist nie gut... und hier auch noch logisch falsch weil du kein Element von deiner "Hardwarelist" ausgewält hast!?
statt "Test.Testplan.Hardware.HardwareList[?].GetHardwarePower.Name" besser so allgemeines per Property in Hardware sicher lösen, wo es zur Vorabfrage auch einen Count gibt. also "if Test.Testplan.Hardware.HardwareList.Count>0 then Caption:=Test.Testplan.Hardware.HardwareList[0].Name" In deiner Änderung liegt glaube ich ein Missverständnis vor. Die HardwareList kann diverse Messgeräte enthalten. z.B. eins für die Leistungsmessung, eins für die Geschwindigkeits- und Wegmessung, ... Deshalb greife ich nicht über HardwareList[0].Name auf ein Element zu, sondern lasse mir das passende Element über die Funktionen in THardwareList (GetHardwarePower, GetHardware,Speed, ...) ermitteln. Rein logisch sehe ich mit aktuellen Angaben auch noch keinen Grund für Test->Testplan.. ausser es kann/soll mal innerhalb eines Tests mehrere Testpläne geben?
Testplan: Enthält enthält was mit welcher Toleranz und welchem Messmittel zu testen ist Test: Ist ein abgeschlossener Test für eine Charge und enthält, mit welchem Testplan gemessen werden soll und die Messresultate "if (Testplan.Count>0) and (Testplan[0].Hardware.Count>0) then Caption:=Testplan[0].Hardware[0].Name"
...das sähe dann für mich schon viele schöner und praktischer aus und du sparst dir die Assigned-Kette weil es ist immer bis zur Liste alles vorhanden(legt man im "contruktor" an und gibt es im "destruktor" frei), nur ob Elemente in der Liste ist per ".count" zu prüfen wenn direkt auf ein Element zugegriffen werden soll. Beispiel für Ausnahme: Aus irgendeinem Grund wurde eine Hardware in der Datenbank gelöscht nachdem ein Test angelegt wurde. Ganz klar, dürfte nie passieren, aber in der DB gibt es keine Constraints, die das verhindern Bei THardware gibt es noch eine andere Einschränkung: Über TTest gibt es in Wirklichkeit noch TBatch. TBatch ist eine Liste von TTest, die durchgeführt werden sollen. Da verschiedene TTest mit der gleichen THardware arbeiten könnten, wird THardware nicht in jedem TTest created sondern liegt in einer THardwareList. Bei gleicher THardware zeigen also zwei TTest auf das gleichen THardware-Elemente. Da habe ich dann schon mehr Skrupel, in der THardwareListe ein Leerdummy zu erstellen. Grüße Gerd |
Zitat |
Registriert seit: 3. Jun 2010 1.611 Beiträge Delphi 10.3 Rio |
#4
Ohne mich jetzt ganz tief mit deinem Beispiel Source (natürlich habe ich ihn mir angeschaut ) beschäftigt zu haben werfe ich mal noch die Stichworte Interfaces und - für euch dann auch ganz gut - Factories in den Raum. Zumindest würde ich das für Sinnvoll empfinden, wenn ich mir deine folgende Aussage durchlese:
Zitat von norwegen60:
In deiner Änderung liegt glaube ich ein Missverständnis vor. Die HardwareList kann diverse Messgeräte enthalten. z.B. eins für die Leistungsmessung, eins für die Geschwindigkeits- und Wegmessung, ... Deshalb greife ich nicht über HardwareList[0].Name auf ein Element zu, sondern lasse mir das passende Element über die Funktionen in THardwareList (GetHardwarePower, GetHardware,Speed, ...) ermitteln.
Wenn dann verschiedene Geräte tiefer gehende Tests benötigen, dann gibt es noch ein erweitertes Interface ITestSpeed welches dann von der betreffenden Klasse implementiert wird. Das Interface selbst kann dann noch von ITest abgeleitet werden. Also so: Interface ITestSpeed(Test) |
Zitat |
Registriert seit: 6. Feb 2008 838 Beiträge |
#5
..."Über TTest gibt es in Wirklichkeit noch TBatch. TBatch ist eine Liste von TTest,"... (wieder die Frage wirklich ?IST? oder ?ENTHÄLT/VERWALTET? oder ?BENUTZT? eine Liste von TTest)
..."Da verschiedene TTest mit der gleichen THardware arbeiten könnten, wird THardware nicht in jedem TTest created sondern liegt in einer THardwareList. Bei gleicher THardware zeigen also zwei TTest auf das gleichen THardware-Elemente"... => Sorry aus deiner anfangs als Vorgabe aufgezeigten Klassenhirarchie erschließt sich das zuletzt geschriebene nicht, daher war aktuell jeder Vorschlag zur Klassenstruktur im Prinzip reines Glaskugel reiben... -> male es als VOLLSTÄNDIGE Struktur und beschreibe deren reale Nutzung im Programm, also auch die Logik wer Eigentümer und Verwalter von Listenobjekten ist, das ist ja oft nicht der welcher ein Objekt zu einer Liste hinzufügt, vor allem wenn es ein globales "mehrfach nutzbares" Objekt geht. -> achte diesmal sehr genau auf deine Formulierung zu "Listen"... "ist eine Liste von X","besitzt/verwaltet eine Liste von X" oder "oder enthält eine Liste von externen X" ist jeweils was total anderes! (Bei der sauberen Lösung hilft das Vermeiden simpler Listenklassen als "ist reine StandardListe von X", da die interne Verwaltung speziell beim Anlegen und Löschen bei jeder Nutzung hier überall und extra separat programmiert werden muss) -> Wenn du UML kannst, male und schreibe es in UML Syntax, wenn dir SQL besser liegt erzeuge mal ein Datenmodell in 3. Normalform, welches alles redunanzfrei abbildet (braucht du eigentlich eh, wenn du Anfangs deine Setups und Vorgaben einlesen und anschließend deine Resultate abspeichern willst) -> wenn du das hast, ergibt sich die Klassenstruktur automatisch wenn du einen guten Weg findest, die sagen wir "globalen" CoreObjekte all deinen Klassen sauber zur Verfügung zu stellen... manche machen das über (mehrere) Interfaces, ich würde im einfachstem Fall in jedem Konstruktor (m)einen CoreContainer(der alles globale enthält und verwaltet) übergeben Mehr wie diese paar allgemeinen Sachen kann ich da erstmal nicht mehr sagen. |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |