![]() |
Vermeiden von globalen Variablen
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo zusammen,
da ich noch ein Neuling in Delphi bin, habe ich die letzten Tage zum Üben ein Programm geschrieben, das in gewisser Hinsicht den Explorer von Windows nachahmt. Die grafische Oberfläche habe ich als Bild angehängt. Genauer gesagt soll mein Programm folgendes machen: Ich habe einen Button ("Verzeichnis wählen"). Klickt man ihn an, kann man ein Verzeichnis wählen. In der ScrollBox, werden dann alle Bilder mit bestimmten Dateiformaten angezeigt, die sich in diesem Verzeichnis befinden. Angefangen habe ich damit, um mit dem Resize-Event zu "spielen", wenn ich nämlich die Breite des Formulars und damit die Breite der ScrollBox ändere, dann sollen die Elemente darin (anfangs waren das einfach nur Panels ohne einen Inhalt. Die Panels erkennt man, wenn man - wie im Bild im Anhang - die Bilder mit gedrückter Maustaste selektiert, die Panels sind dann blau) entsprechend neu angeordnet werden, so dass man auf keinen Fall horizontal scrollen muss. Um das zu erreichen, habe ich erst einmal ein paar globale Variablen definiert (obwohl ich globale Variablen nicht mag, da sie ein Programm fehleranfällig machen). In diesen Variablen ist dann z. B. gespeichert, wie weit die linke Spalte der Panels vom linken Rand der Scrollbox entfernt ist, wie groß die Panels überhaupt sind, wie groß die horizontalen und vertikalen der Abstände der Panels zueinander sind etc. Das kann man so machen, wenn man für jedes Panel gleiche Werte verwenden möchte wie ich in meinem Programm. In einem weiteren Schritt habe ich Objekte der Klasse TImage auf die Panels gesetzt, worin die Bilder angezeigt werden. Auch hierfür habe ich wieder globale Variablen verwendet, um die Abstände des Bildes zu den Rändern des Panels festzulegen. Im nächsten Schritt habe ich unten noch Labels auf die Panels gelegt, um den Dateinamen anzuzeigen. Auch hier gibt es wieder globale Variablen. Ich wollte nun noch Edit-Felder über die Panels legen, um ggf. den Dateinamen zu ändern (das ganze sollte am Ende eben ähnlich aussehen wie in Windows). Da der Quellcode inzwischen relativ unübersichtlich geworden ist, habe ich mir heute Gedanken darüber gemacht, "meine" Panels als eine eigene Klasse zu definieren. Dann spare ich mir den ganzen Aufwand in meinem Programm, die Eigenschaften von den TImage-Komponenten, den Labels etc. zu setzen. Dann brauch ich ledigich noch Bilder inkl. Dateinamen an die Instanzen meiner Klasse (die ich TIconPanel genannt habe) zu übergeben und die Klasse soll dann den Rest für mich erledigen. Jetzt bin ich aber auf 2 Probleme gestoßen, die mich schon den ganzen Nachmittag aufgehalten haben, weil ich noch sehr unerfahren im Programmieren bin und nicht weiß, wie man es am besten implementieren sollte: 1. Ich möchte die vielen globalen Variablen weg haben, aber die Daten müssen ja trotzdem in irgendeiner Weise zur Verfügung gestellt werden. Wenn man die Werte für die Abstände etc. variabel halten möchte, würde ich einfach Attribute definieren, die dann jede Instanz der Klasse TIconPanel trägt. Dann kann man über über Properties darauf zugreifen und ggf. die Werte ändern. Eine andere Möglichkeit wäre (wenn man die Abstände, Größen etc. konstant halten möchte), keine Set-Methoden zu implementieren, so dass man nur zum Lesen auf die Attribute zugreifen kann. Die Werte werden im Konstruktor festgelegt und bleiben immer gleich, solange die Werte niemand im Quellcode ändert ;) Was ich mir als erstes überlegt habe (und ich habe es auch so implementiert, aber das hat der Übersichtlichkeit geschadet), war, die globalen Variablen in Form von Klassenfeldern zu definieren und dann mit Klasseneigenschaften darauf zuzugreifen. Diese Idee schien mir am naheliegensten, weil ich erst einmal alle Abstände, Größen etc. konstant halten wollte, d. h. diese Parameter sind nicht individuell für jede Instanz sondern sollen für die gesamte Klasse "gelten", es sind also Eigenschaften der Klasse, nicht der Instanzen. Nachdem ich es implementiert habe, habe ich jedoch festgestellt, dass das kaum etwas an meiner Programmstruktur geändert hat. Ich habe beim Öffnen des Formulars einfach den Klasseneigenschaften die gewünschten Werte zugewiesen und dann eben diese Klasseneigenschaften anstatt den globalen Variablen verwendet. Mir kamen diese Klasseneigenschaften jedoch genau wie die gloaben Variablen vor, nur dass ich eben nicht extra Variablen in meinem Formular definieren musste, sondern die "globalen" Klasseneigenschaften der Klasse verwendet habe. Ich hoffe, dass ich mein Problem klar genug darstellen konnte und mir jemand einen Rat geben kann. Mir geht es hier nicht darum, dass mir jemand Quelltext schreibt (außer, es hilft beim Verständnis ;)), sondern darum, welches Prinzip sich am besten eignet, mein Problem zu implementieren, das Programm werde ich später vmtl. sowieso nie brauchen :D Evtl. gibt es noch eine andere gute Möglichkeit, an die ich noch gar nicht gedacht habe. 2. Nun zu meinem zweiten Problem: Eine meiner globalen Variablen ist ein "array of TPanel", das die Panels, die angezeigt werden sollen, enthält. Damit kann man leicht arbeiten: Man kann auf einzelne Panels mittels Index zugreifen, man kann Panels hinzufügen, löschen, die Sortierung ändern etc. Deshalb möchte ich diese Art der Datenspeicherung gar nicht aufgeben. Trotzdem stellt sich mir die Frage, ob es sinnvoll ist, auch hier die globale Variable durch etwas anderes zu ersetzen. Ich hatte wieder die Idee, der Klasse TIconPanel ein array of TIconPanel als Klassenfeld zu übergeben. Beim Konstruktor der Klasse soll die erzeugte Instanz ans array angehängt werden, beim Destruktor wieder entfernt werden. Ist es sinnvoll, das so zu machen? Ich finde es einerseits sinnvoll, andererseits habe ich dann keine Möglichkeit, in meinem Formular z. B. eine zweite Scrollbox ebenfalls mit TIconPanels als Inhalt hinzuzufügen (die z. B. Bilder eines anderen Verzeichnisses anzeigt), weil ich nur ein array für die Klasse habe. Was erscheint euch hier am sinnvollsten? Kann man überhaupt allgemein sagen, welche Art der Implementierung optimal ist, ohne jetzt schon genau zu wissen, wohin das Programm noch führt und welche Anforderungen ich an die Klasse später vllt. einmal habe? Sollte ich mich jetzt schon endgültig festlegen, wofür ich die Klasse am Ende brauche und was sie können muss, um nun eine "gute" Implementierung zu finden? Über einen Rat wäre ich auch hier sehr froh. Schon jetzt vielen Dank an alle, die sich das durchgelesen haben und mir evtl. helfen :) |
AW: Vermeiden von globalen Variablen
Zitat:
Viele hier im Forum erfassen ein Problem schneller, wenn sie etwas Code zu lesen bekommen. |
AW: Vermeiden von globalen Variablen
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Zitat:
Im Prinzip kann man sich in der Datei im Anhang auf die Prozedur TfrmMain.btnShowImagesClick(Sender: TObject) konzentrieren. Darin passiert das Wichtigste. |
AW: Vermeiden von globalen Variablen
Die globalen Variablen wirst du zumindest formell einfach los, wenn du sie als Felder innerhalb der Form-Klasse anlegst. Das verbessert aber die Struktur nur unwesentlich.
Anstatt eine neue Panel-Klasse zu deklarieren (was natürlich auch geht), kannst du auch einen Frame anlegen (Datei - Neu - Weitere... - Delphi-Projekte - Delphi-Dateien - VCL-Frame). Darin platzierst du das Image, das Label und das Edit und was du sonst noch alles brauchst. Hier kannst du auch mit dem Align, AlignWithMargin, Margins und Padding der einzelnen Controls spielen um das gewünschte Layout zu erreichen. Im Form erzeugst du dann kein TPanel sondern eben einen solchen Frame (Frame-Unit in Uses aufnehmen) und platzierst ihn an die passende Stelle. Hier wäre eventuell ein TFlowPanel als Parent brauchbar, das sich automatisch um die fließende Anordnung kümmert. Das würde deinen Code sicher deutlich reduzieren. Ich kann das jetzt leider nicht so genau beschreiben, wie du das gern machst. Also musst du sicher noch einiges durch Ausprobieren herausfinden. |
AW: Vermeiden von globalen Variablen
Um mal allgemein zu antworten. Globale Variablen wird man am ehesten los, wenn man mit Klassen arbeitet. Dort können die Werte mit Hilfe von privaten Feldern gut den einzelnen Methoden zu Verfügung gestellt werden. Im Konstruktor werden sie zu gewiesen/belegt und stehen dann den Methoden zur Verfügung.
Denk eventuell mal in diese Richtung. |
AW: Vermeiden von globalen Variablen
Danke für die Antworten! Das hilft mir schon einmal weiter.
Zitat:
Zitat:
Code:
Ähnlich wie im Explorer von Windows möchte ich, dass selektierte Panels blau (selectedPnlColor) werden. Wenn nun schon andere Panels selektiert sind, dann sollen diese Panels nach einem Klick wieder die Standardfarbe (defaultPnlColor) bekommen (falls STRG nicht gedrückt wird). Wie im Code oben zu sehen, greife ich hierzu auf panelsArray zu, das array of TPanel, das alle Panels in meiner Scrollbox enthält.
procedure TfrmMain.SelectByClick(Sender: TObject);
var obj : TPanel; pnlIdx : Integer; begin obj := Sender as TPanel; if GetKeyState(VK_Control) < 0 then // Strg pressed; enable multiselect begin if obj.Color = selectedPnlColor then obj.Color := defaultPnlColor else obj.Color := selectedPnlColor; end else // Strg not pressed; disable multiselect begin for pnlIdx := 0 to Length(panelsArray) -1 do begin panelsArray[pnlIdx].Color := defaultPnlColor; end; obj.Color := selectedPnlColor; end; end; Wenn ich eine solche Prozedur in meiner Klasse implementieren möchte, dann brauche ich also in meiner Klasse schon ein array in Form einer Klassenvariablen. In diesem Fall hätte ich vmtl. kein Problem, die Prozedur zu implementieren. Aber das schränkt meine Klasse dafür stark ein. Ich kann in meinem Projekt ohne Weiteres nur eine Scrollbox mit Panels erzeugen. Ist es in meinem Fall sinnvoll, das array als Klassenvariable zu definieren oder gibt es noch andere Möglichkeiten, um die Prozedur im Code-Beispiel oben auch ohne Klassenvariable innerhalb meiner Klasse zu implementieren. Vielleicht beantworte ich meine Frage jetzt schon selbst, aber als ich den Beitrag geschrieben habe, kam mir die Idee, meiner Klasse TIconPanel eine weitere Eigenschaft mitzugeben, z. B. ein Integer FGroupIndex. Dann könnte ich IconPanels, die zusammen gehören sollen, allen den gleichen Gruppen-Index verpassen und so aus allen Panels im array die heraussuchen, die einen bestimmen Wert bei FGroupIndex haben. P.S.: Zusammenfassend kann man vllt. sagen, dass meine "Probleme" neben meiner fehlender Erfahrung auch daher kommen, dass ich mich nicht entscheiden kann, ob ich meine Klasse allgemein halten möchte oder spezieller machen möchte. Ist sie allgemein, dann kann ich die Klasse für viele verschiedene Anwendungen verwenden, dafür erledigt die Klasse wenig Arbeit für mich. Implementiere ich die Klasse hingegen eher spezieller, dann bin ich bei der Verwendung stärker eingeschänkt, aber dafür erledigt die Klasse schon fast alles für mich. |
AW: Vermeiden von globalen Variablen
Zitat:
Klar, deine Anforderungen werden sich ändern. Aber meistens sind das dann inkrementelle Änderungen (xyz soll hier ne Spezialbehandlung bekommen, also ein Feature wie "Wenn jemand Freitag den 13. im Kalender auswählt, soll hier ein roter Warnhinweis erscheinen") und nicht solche Sachen wie "Wir möchten eine Datenbank in der man in einem bestimmten Format Warnungsregeln für beliebige Kalendertage definieren kann." - so eine Anforderung ist meistens früh bekannt. Und wenn du vorzeitig "universell einsetzbaren" Code schreibst, dann sind in deiner Anwendung viele Stellen, in denen du dann wieder einen Spezialfall bauen musst. An der Stelle sieht das dann umständlich aus. Wenn du etwas vielleicht in anderen Projekten auch mal brauchen kannst, dann programmiere das erst mal so, dass es aktuell passt. Für das andere Projekt kannst du ja die Klasse kopieren und erweitern (wenn du genau weißt, was für eine Erweiterung du jetzt brauchst). |
AW: Vermeiden von globalen Variablen
Delphi-Quellcode:
Dies sieht ja so aus, als ob du über die Panel-Farbe feststellst, ob eine Datei (für die ja das Panel steht) selektiert ist oder nicht. Hier in der DP wird dir sicher noch sehr oft gepredigt (nicht negativ gemeint) werden, dass es sinnvoll ist, die Logik von der Darstellung zu trennen. Hier könnte das so aussehen, das du unabh. von der GUI eine Liste von Klassen hast, jede steht für eine Datei und in der Klasse könnte es eine Eigenschaft Selected geben. Oder du hast eine Liste aller Klassen/Dateien und eine in der die selektierten Klassen/Dateien stehen.
if obj.Color = selectedPnlColor then obj.Color := defaultPnlColor
else obj.Color := selectedPnlColor; Wird nun in der GUI geklickt schaust du welche Datei/Klasse dies betrifft. Schaust ob sie bereits selektiert ist oder nicht und änderst diesen Zustand entsprechend. Anschließend wird die GUI "refreshed". |
AW: Vermeiden von globalen Variablen
@Jumpy & TE
Gerade in Bezug auf die Controls wird hier explizit gepredigt, dass diese Controls zur Darstellung eines Status sehr wohl geeignet sind aber eben nicht um den Status-Wert dort abzulesen bzw. als Status-Speicher zu missbrauchen. Daten präsentieren: Ja Daten speichern: Nein Grund: Die Präsentation ist immer eine Interpretation von Daten. Um von dem interpretierten und präsentierten Wert wieder auf den Ursprungswert zu kommen ist durch die Brust ins Auge, aufwändig und somit auch extrem fehleranfällig. Eine Liste mit den echten Informationen und diese Liste einfach in den Controls abbilden ist erheblich einfacher, als von dem Control wieder die richtigen Daten heraus zu interpretieren. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:40 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