![]() |
Probleme mit selbst erstellter Klasse
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,
erstmal entschuldigung für die schlechte Überschrift, aber ich weis wirklich nicht wie ich mein Problem betiteln soll. Es geht um folgendes, in der Schule lerne ich zur Zeit Delphi und wir sollten eine (Warenhaus-)Kasse simulieren. Dazu sollte eine Schlange(FIFO, TSchlange) als Klasse erzeugt werden, welche dann Kunden(TKunde) Verwaltet. Die Kunden werden vom Hauptformular in bestimmten Abständen erzeugt und von Zeit zur Zeit werden die Kunden "bedient" indem ihrem Warenkorb ein Gegenstand entnommen wird und "gescannt" wird (Verringerung der TKunde-Variable "Anzahl" um 1). Das ganze wird in einer ListBox dargestellt. Es sollte dann so ablaufen, dass wenn der Kunde keinen Gegenstand mehr im Warenkorb hat (Anzahl = 0) der Kunde "bezahlt" (destroy) und der nächste Kunde bedient wird. Bis dahin bin ich leider erst gar nicht gekommen. Bei mir tritt nämlich folgender Fehler auf: Anstatt, dass ein neuer Kunde erzeugt und in die Schlange eingereiht wird (enqueue), wird das Aktuelle Element manipuliert. Ich habe schon ziemlich viel Zeit darauf verbracht den Fehler zu finden. Doch weder durch meinen Lehrer noch durch mich konnte der Fehler behoben werden. Ich habe mien Programm angehängt. Ich hoffe, dass ihr mir helfen könnt. Vielen Dank >X!< |
Re: Probleme mit selbst erstellter Klasse
Hallo,
ich habe mir dein Projekt herunter geladen. Am Quelltext erkenne ich, dass du wirklich noch nicht viel programmiert hast, aber da sind auch noch fundamentale Fehler. Ich habe dein Programm einfach mal gestartet und mir das Ergebnis angesehen. Also das Anstellen an der Schlange scheint auf den ersten Blick zu funktionieren, die Kunden werden immer mehr, der Kassierer kommt einfach nicht nach. Wenn der Sinn der Übung darin besteht, eine FIFO-Struktur zu implementieren (die Klasse Contnrs.TQueue kann alles was du brauchst), dann solltest du das auch tun, bevor du zu Kunden und Kasse übergehst. Für die FIFO-Struktur kannst du wählen:
Grüße vom marabu |
Re: Probleme mit selbst erstellter Klasse
Hallo,
erstmal vielen Dank für die Mühe. Dass alle Variablen, die in ner Klasse unter var stehen, global sind wusste ich bisher nicht. Ausserdem wurde uns die einfach verkettete Liste vorgegeben, wobei mir arrays eigentlich lieber sind. Du hast fundamentale Fehler erwähnt. Könntest du das bitte ein wenig konkretisieren?! Da ich noch am Anfang mit Delphi stehe und meine Begegnungen mit Delphi fast ausschließlich nur theoretischer Natur waren (also, lesen, verstehen, im Unterricht wiedergeben), bin ich grad erst dabei mir praktische Erfahrung anzueignen. Wohin das führt, hast du ja gesehen :-D >X! |
Re: Probleme mit selbst erstellter Klasse
Der fundamentale Fehler schlechthin ist die Sache mit den globalen Variablen Nach und Anzahl. Anzahl gehört eigentlich zum Gedächtnis deines Kunden-Objektes. Mit dem Verweis auf den Nachfolger ist das allerdings so eine Sache. Der Grad der Entkopplung deiner Klassen bestimmt wo dieser Verweis unterzubringen ist.
Denkbar sind folgende Ansätze: (1) Deine Queue ist spezifisch und kann nur Objekte einer bestimmten Klasse verwalten. (2) Deine Queue ist generisch und kann Objekte einer beliebigen Klasse verwalten. (2.1) Die Klasse TKunde ist Nachfahre einer speziellen Klasse TQueueItem. (2.2) Die Klasse TQueueItem enthält einen Verweis auf ein Kunden-Objekt. Ich favorisiere Ansatz (2) und bei deiner Aufgabe dürften die beiden dort unterschiedenen Varianten beide problemlos umsetzbar sein.
Delphi-Quellcode:
// Variante (2.1)
type TQueueItem = class private FNext: TQueueItem; public property Next: TQueueItem read FNext write FNext; end; TKunde = class(TQueueItem) private FAnzahl: integer; public constructor Create(anzahl: integer); procedure Auflegen; // Einen Artikel zum Erfassen geben property Anzahl: integer read FAnzahl; // write nur intern property Naechster: TKunde read FNext; end;
Delphi-Quellcode:
Früher oder später solltest du dir angewöhnen abstrakte Klassen (Queue) und solche aus deinem Aufgabenfeld (Kunde, Kasse) voneinander zu trennen. Hier noch ein Code-Schnippsel für TQueue:
// Variante (2.2)
type TQueueItem = class private FNext: TQueueItem; FData: TObject; public consturctor Create(data: TObject); property Data: TObject read FData; property Next: TQueueItem read FNext write FNext; end; TKunde = class private FAnzahl: integer; public constructor Create(anzahl: integer); procedure Auflegen; // Einen Artikel zum Erfassen geben property Anzahl: integer read FAnzahl; // write nur intern end;
Delphi-Quellcode:
Ein anderer Fehler ist deine Verwendung von Randomize(). Diese Prozedur dient eigentlich dazu dem Pseudo-Zufallszahlen-Generator einen bestimmten Seed-Wert zu verpassen, damit er eine reproduzierbare Folge von Zufallszahlen erzeugt. Brauchst du eigentlich nicht wirklich - schon gar nicht im Konstruktor deiner Kunden-Klasse.
TQueue = class
private FHead: TQueueItem; FTail: TQueueItem; public function Add(qi: TQueueItem): TQueueItem; function Remove: TQueueItem; property Head: TQueueItem read FHead; end; Bevor ich deine Hausaufgaben jetzt ganz erledige warte ich lieber mal darauf, was du so zu Stande bringst. marabu |
Re: Probleme mit selbst erstellter Klasse
Also, eigentlich kriegen wir die Deklarationen aus nem Buch und müssen die Methoden dann nur "füllen",
was manchmal ziemlich in die Hose geht, da die Methoden kaum beschrieben werden und die Namen auch nicht gerade aussagekräftig sind. Ich hab versucht die Kassen so selbstständig wie möglich zu machen und das ist das Ergebnis:
Delphi-Quellcode:
unit mKasse;
interface uses Classes, StdCtrls, ExtCtrls, mKunde; type TSchlange = class(TObject) private fAktuell,fEnde :TKunde; TimKunde, TimBedienung: TTimer; fLstKasse: TListBox; function IsEmpty: boolean; virtual; procedure TimKundeTimer(Sender:TObject); procedure TimBedienungTimer(Sender:TObject); procedure Verwaltung(P: boolean); virtual; public constructor Create(List: TListBox); virtual; procedure Enqueue(Elem: TKunde); virtual; procedure Dequeue; virtual; property Element: TKunde read fAktuell; property Ende: TKunde read fEnde; property Offen: Boolean write Verwaltung; end; implementation constructor TSchlange.Create(List: TListBox); begin fLstKasse := List; TimKunde := TTimer.Create(nil); With TimKunde do begin Interval := 1000; Enabled := false; OnTimer := TimKundeTimer; end; TimBedienung := TTimer.Create(nil); With TimBedienung do begin Interval := 250; Enabled := false; OnTimer := TimBedienungTimer; end; end; procedure TSchlange.Enqueue(Elem: TKunde); var SAnzahl : String; begin if not self.IsEmpty then Ende.Nach := Elem else fAktuell := Elem; fEnde := Elem; Str(fEnde.Anzahl,SAnzahl); fLstKasse.AddItem(SAnzahl,self); end; procedure TSchlange.Dequeue; var temp: TKunde; begin if not self.IsEmpty then begin; temp := fAktuell; fAktuell := temp.Nach; temp.Destroy; if fAktuell = nil then fEnde := nil; end; end; function TSchlange.IsEmpty: boolean; begin If fAktuell <> nil then IsEmpty := false else IsEmpty := true; end; procedure TSchlange.TimKundeTimer(Sender:TObject); begin Enqueue(TKunde.Create); end; procedure TSchlange.TimBedienungTimer(Sender:TObject); var num: string; begin if not IsEmpty then begin fAktuell.Gescannt; fLstKasse.Items.Delete(0); if fAktuell.Anzahl = -1 then Dequeue else begin str(fAktuell.Anzahl,num); fLstKasse.Items.Insert(0,num); end; end; if (IsEmpty and (TimKunde.Enabled = false)) then TimBedienung.Enabled := false; end; procedure TSchlange.Verwaltung(P: boolean); begin TimKunde.Enabled := P; if p = true then TimBedienung.Enabled := p; end; end.
Delphi-Quellcode:
Die Kasse stellt so gesehen selber Kunden an und bearbeitet diese,
unit mKunde;
interface type TKunde = class(TObject) private fanzahl :integer; fNach : TKunde; public constructor Create; virtual; procedure Gescannt; property Anzahl: Integer read fAnzahl; property Nach: TKunde read fNach write fNach; end; implementation constructor TKunde.Create; begin inherited create; randomize; fanzahl := random(9) +1 end; procedure TKunde.Gescannt; begin fanzahl := fanzahl -1; end; end. so dass das Formular nur die ListBox bereitstellen muss. Es funzt, aber ich schätze mal, dass die Lösung nicht gerade elegant ist. :oops: |
Re: Probleme mit selbst erstellter Klasse
Ich kann jetzt nicht genau erkennen, was Vorgabe ist und nicht verändert werden darf. Du sagst auch, dass dein Code funktioniert. Deshalb gebe ich nur noch ein paar zaghafte Hinweise.
Denke an das, was ich dir zu Randomize geschrieben habe. Du kannst diese Funktion einfach weglassen. Nur wenn du eine Testreihe öfter wiederholen willst, solltest du die Initialisierung mit identischem Seed-Wert an geeigneter Stelle vornehmen - z.B. vor dem Start der message pump im Projektquelltext. Deine Timer sind ganz gewiss keine Eigenschaften einer Kasse, sondern eigentlich nur Hilfsmittel für die Ablaufsteuerung. Ich würde die Timer bei der Form ansiedeln. Noch besser wäre auf sie zu verzichten und die Erzeugung von neuen Kunden und den Vorgang des Abkassierens über Buttons interaktiv zu steuern. Dass die Kasse selbst ihre neuen Kunden erzeugt und am Ende der Warteschlange anstellt, ist nicht sehr realistisch - auch wenn es funktionieren sollte. Da wäre eine Kunden-Factory der bessere Ansatz, aber ob der in deinem Buch besprochen wird? Wohl eher nicht. Ist dir eigentlich schon aufgefallen, dass du gar keine Klasse TKasse hast? Gescannt klingt so, als ob dem Kunden vom Kassierer mitgeteilt wird, dass ein Artikel erfasst wurde. Hier wie schon beim Anstellen sehe ich die aktive Rolle eher beim Kunden. Nicht der Kassierer nimmt sich einen Artikel aus dem Warenkorb des Kunden, sondern der Kunde legt einen Artikel zum Scannen auf. Die meisten Kopfschmerzen bei deinem Projekt machen mir die Mängel im Entwurf, aber wenn das Vorgaben aus deinem Buch sind, wirst du wohl am besten so weiter machen. Viel Erfolg marabu |
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:12 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