Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Software-Projekte der Mitglieder (https://www.delphipraxis.net/26-software-projekte-der-mitglieder/)
-   -   Übungsprogramm "Kinokarten" (https://www.delphipraxis.net/191102-uebungsprogramm-kinokarten.html)

EdAdvokat 8. Dez 2016 11:54

Übungsprogramm "Kinokarten"
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ein kleines Beispielprogramm "Kinokarten". Der Nutzen sei mal dahingestellt, doch wären einige Tips füe die Dateneingabe und Verarbeitung für Anfänger vermutlich nicht verkehrt.

Luckie 8. Dez 2016 12:02

AW: Übungsprogramm "Kinokarten"
 
Wäre schön, wenn du noch schreiben würdest, welche Techniken im Einzel demonstriert werden.

EdAdvokat 8. Dez 2016 12:59

AW: Übungsprogramm "Kinokarten"
 
Es handelt sich um ein bescheidenes Beispielprogramm für eine Kinokasse. Es können Kartenbestellungen
für 3 Preisklassen aufgegeben werden, für die das Programm dann den Brutto- und Nettopreis sowie die
Mehrwertsteuer ausgibt. Zugleich wird die Zahl der verkauften Karten und die Zahl der noch freien Plätze
angezeigt.
Also einfache Berechnungen, bedingte Eingaben, Fehlerbehandlung, Exceptions im bescheidenen Umfang sind
das Thema.
Es ist meine erste Veröffentlichung hier.

Ghostwalker 8. Dez 2016 18:35

AW: Übungsprogramm "Kinokarten"
 
Na gut...dann Start ich mal die Meckerrunde :twisted:


Zuerstmal UI (Oberfläche):

- Edit-Felder sollten für die Eingabe verwendet werden..nicht für reine Ausgaben (dafür gibts StaticText/Label...)

- Die Checkboxen für die einzelnen Ränge sind an und für sich Überflüssig. Wenn der User für
eine Preiskategorie eine Anzahl erfasst, will er sie auch berechet haben :)


Zur Umsetzung:

- Das Abfangen der Tasten in den Edit-Feldern ist löblich. Aber a) fehlen einige Tasten (10'er-Tastatur usw.). b) gehts einfacher mit MaskEdit :)

- Statt Anz1, Anz2, Anz3 : integer und R1,R2,R3 : real würd ich Array's verwenden. Damit lässt sich das ganze einfacher erweitern (Das nächste Kino hat 5 Preiskategorien, ein anderes 2...)


- Fixe Werte im Quelltext sollte man vermeiden. Entweder über Konstanten oder, wenn eventuell später Erweiterungen geplant sind, über Config-/Inidateien.

Anmerkung zur Code-Lesbarkeit:


Schwer Lesbar:

Delphi-Quellcode:
   try
  Anz1:=strtoint(Edit2.Text);
  if checkbox1.Checked then R1:=Anz1*15.00
    else R1:=0.0;
  if not checkbox1.Checked then
    Anz1:=(0);

  Anz2:=strtoint(Edit3.Text);
  if checkbox2.Checked then R2:=Anz2*13.00
    else R2:=0.0;
  if not checkbox2.Checked then
    Anz2:=0;

  Anz3:=strtoint(Edit4.Text);
  if checkbox3.Checked then R3:=Anz3*10.00
    else R3:=0.0;
  if not checkbox3.Checked then
    Anz3:=0;

      ZwSu:=ZwSu+R1+R2+R3;
      Steuer:=ZwSu*0.19;
      Endpreis:=ZwSu+Steuer;
      Edit5.Text:=floattostrF(Endpreis, ffCurrency, 8,2);

    showMessage('Der Preis '+floattostrF(Endpreis,ffCurrency, 8,2)+' setzt sich zusammen aus '+floattostrF(zwSu,ffCurrency, 8,2)+' Netto plus '#10#13+floattostrF(Steuer, ffCurrency, 8,2)+' MWSt');
      Gesamt:= Anz1+Anz2+Anz3;
      Kinopl:=Kinopl-Gesamt;
        Edit1.Text:=inttostr(Gesamt);
        edit6.Text:=inttostr(Kinopl);
        zwSu:=0.0;
        Steuer:=0.0;
        Endpreis:=0.0;
  except
    on EConvertError do showMessage(Fehler);
  end;

Besser:

Delphi-Quellcode:
  try
   Anz1:=strtoint(Edit2.Text);
   if checkbox1.Checked then
     R1:=Anz1*15.00
   else
     R1:=0.0;
   if not checkbox1.Checked then
     Anz1:=(0);

   Anz2:=strtoint(Edit3.Text);
   if checkbox2.Checked then
     R2:=Anz2*13.00
   else
     R2:=0.0;
   if not checkbox2.Checked then
      Anz2:=0;

   Anz3:=strtoint(Edit4.Text);
   if checkbox3.Checked then
     R3:=Anz3*10.00
   else
     R3:=0.0;
   if not checkbox3.Checked then
     Anz3:=0;

   ZwSu:=ZwSu+R1+R2+R3;
   Steuer:=ZwSu*0.19;
   Endpreis:=ZwSu+Steuer;
   Edit5.Text:=floattostrF(Endpreis, ffCurrency, 8,2);

   showMessage('Der Preis '+floattostrF(Endpreis,ffCurrency, 8,2)+' setzt sich zusammen aus '+floattostrF(zwSu,ffCurrency, 8,2)+' Netto plus '#10#13+floattostrF(Steuer, ffCurrency, 8,2)+' MWSt');
   Gesamt:= Anz1+Anz2+Anz3;
   Kinopl:=Kinopl-Gesamt;
   Edit1.Text:=inttostr(Gesamt);
   edit6.Text:=inttostr(Kinopl);
   zwSu:=0.0;
   Steuer:=0.0;
   Endpreis:=0.0;
  except
    on EConvertError do showMessage(Fehler);
  end;

Der schöne Günther 8. Dez 2016 19:34

AW: Übungsprogramm "Kinokarten"
 
Zitat:

Zitat von Ghostwalker (Beitrag 1355738)
Edit-Felder sollten für die Eingabe verwendet werden..nicht für reine Ausgaben (dafür gibts StaticText/Label...)

Finde ich überhaupt nicht. Aus einem Edit-Feld kannst du komfortabel den Inhalt markieren und kopieren. Mit einem Label nicht.

EdAdvokat 8. Dez 2016 19:58

AW: Übungsprogramm "Kinokarten"
 
Also konstruktive Kritik ist nützlich.
Es ist mein erstes Programm, dass ich je hochgeladen habe, obwohl ich so ganz jung nicht mehr bin.
Es war von Anfang an als Beispielprogramm geplant, um einiges auszuprobieren, aus Spaß an der Freude aber nie für
einen realen Zweck.
Sicher sind die Checkboxen nicht wirklich nötig, doch die Spielerei mit den Bedingungen fand ich nicht schlecht.
Ja eine Eingabe über Maskedit wäre besser, doch so hätte ich auf die Eingabenbehandlung verzichten müssen und das
wollte ich eigentlich nicht. Was mit der 10-er Tastatur gemeintist, habe ich nicht verstanden. Ich dachte alles erfaßt zu haben.
Die Anregung arrays zu verwenden ist ok. So wäre das Ganze besser ausbaufähig, werde ich mir merken.
Die Lesbarkeit des Quelltextes ist immer verbesserungsmöglich.
Auch meine Ausgabe über ShowMessage ist nicht wirklich der Hit, dass muss ich gegen mich hier mal feststellen.
Ich hätte gar nicht gedacht, dass so ein kleines "Programmchen" so viel Beachtung findet.
Es hilft mir als reiner Hobby-Programmierer wirklich weiter. Vielen Dank

Aviator 8. Dez 2016 21:25

AW: Übungsprogramm "Kinokarten"
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1355739)
Zitat:

Zitat von Ghostwalker (Beitrag 1355738)
Edit-Felder sollten für die Eingabe verwendet werden..nicht für reine Ausgaben (dafür gibts StaticText/Label...)

Finde ich überhaupt nicht. Aus einem Edit-Feld kannst du komfortabel den Inhalt markieren und kopieren. Mit einem Label nicht.

Ich auch nicht. Und wenn man den Text kopieren will und es aussehen soll wie ein Label, dann stellt man einfach den BorderStyle auf bsNone. :-D

himitsu 8. Dez 2016 21:33

AW: Übungsprogramm "Kinokarten"
 
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:

Zitat von Aviator (Beitrag 1355741)
Ich auch nicht. Und wenn man den Text kopieren will und es aussehen soll wie ein Label, dann stellt man einfach den BorderStyle auf bsNone. :-D

Siehe der Dateieigenschaftendialog des Explorers :zwinker:

Zitat:

Delphi-Quellcode:
var
  Anz1, Anz2, Anz3, Gesamt, Kinopl : integer;
  R1, R2, R3, zwSu, Steuer, Endpreis : real;

Gerade als "Beispielprogramm", da sollte schon jemand für die globalen Variablen gesteinigt werden.
Ganz schlimm daran ist vorallem, dass viele dieser Variablen auch nur für lokale Zwischenergebnisse verschwendet verwendet werden.

Zitat:

Delphi-Quellcode:
const Fehler=('Es ist ein Eingabefehler aufgetreten. Bitte prüfen!');

try

except
  on EConvertError do showMessage(Fehler);
end;

Was ist mit anderen Fehlern/Exceptiontypen?

Warum manuell anzeigen, wenn Exceptions sowieso automatisch angezeigt werden?
Vorallem wenn man dann die eigentliche aussagefähige Fehlermeldung "vernichtet", in welcher sogar der fehlerhafte Wert genannt wurde, der in irgendeinem der vielen Edits steht.


Und dann noch die total aussagefähigen Bezeichner.
Edit1, CheckBox1, ...

Gut, der fehlende Ziffernblock wurde schon genannt, aber da gibt es noch einen anderen Bug. :twisted:
Delphi-Referenz durchsuchenVcl.StdCtrls.TEdit.NumbersOnly

EdAdvokat 9. Dez 2016 18:36

AW: Übungsprogramm "Kinokarten"
 
vielen Dank für die Anregungen.
"Der eine meint, er wäre fertig, doch die anderen sehen, er beginnt erst..."
NumbersOnly in den EditFeldern habe ich auf true gesetzt und hoffe damit, dass das "ich bin böse"-Phänomen damit nicht mehr auftreten kann.
Es lassen sich nun tatsächlich nur Zahlen eingeben. (Tastatur und Nummernblock). Ich habe noch immer nicht verstanden was ich ändern sollte hinsichtlich des Nummernblocks, 10-er Tastatur??? Welche falschen Eingaben müssen also noch abgefangen werden? Was ist damit gemeint?
Natürlich muss eine Überwachung der verkauften Karten in Bezug auf die Gesamtzahl der Plätze erfolgen. Mehr noch es muss unterteilt werden,
wieviel Karten im 1.,2. und 3. Rang vorhanden bzw. schon verkauft wurden. Das wird recht unübersichtlich, doch ich arbeite daran.
Die globalen Variablen wurden bereits in lokale umgewandelt und auch haben die Bezeichner aussagekräftigere Namen erhalten.
Auf die CheckBoxen habe ich verzichtet. Noch läuft es nicht ganz rund.

himitsu 9. Dez 2016 21:25

AW: Übungsprogramm "Kinokarten"
 
Zitat:

Zitat von EdAdvokat (Beitrag 1355804)
NumbersOnly in den EditFeldern habe ich auf true gesetzt und hoffe damit, dass das "ich bin böse"-Phänomen damit nicht mehr auftreten kann.

ähhhhhhh, naja :lol:
Aber es steht zumindestens in der Hilfe, dass Delphi Windows das Problem nicht gelöst, aber zumindestens dokumentiert hat.
Zumindestens macht das den eigenen Code "einfacher" und der Nummernblock wurde beachtet.

Im Prinzip müsste man "alle" möglichen Quellen für Text prüfen/behandeln.
Du kannst Text zuweisen (
Delphi-Quellcode:
Edit.Text := 'abc'
), man kann einfügen (Copy&Paste) und das per mehreren ShortCuts (Strg+V, Shift+Einfg, ...), über das Kontextmenü und mehrere andere WindowsMessages.
Aber der sicherste Weg ist, wenn man auch noch im OnChange den Text auf ungültige Zeichen prüft.
Problem hierbei ist aber, dass "kurzzeitig" auch mal "ungültige" Werte erlaubt sein sollte, drum da nur optische/farbige Meldungen und erst im OnExit und beim Auslesen schärfer prüfen.
Das gilt vorallem dann, wenn z.B. auch das Vorzeichen und Tausender- oder Dezimaltrennzeichen drin vorkommen dürfen.

EdAdvokat 12. Dez 2016 13:05

AW: Übungsprogramm "Kinokarten"
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hier nun mein zweiter Versuch "Kinokarten" mit Karten für den 1. bis 3. Rang und Bedingungen für die Platzanzahl in Bezug auf die verkauften Karten.
Ich hoffe den Hinweis Arrays zu verwenden habe ich richtig verstanden und einige weitere Veränderungen haben das kleine Übungsprogramm etwas aufgewertet.

EdAdvokat 14. Dez 2016 13:07

AW: Übungsprogramm "Kinokarten"
 
Liste der Anhänge anzeigen (Anzahl: 1)
So perfekt kann das Programm doch nicht gewesen sein, dass bislang keine Kritik folgte. Hier nun meine vorerst letzte Version mit einigen zusätzlichen Funktionen wie Kontextmenü und Preise für ermäßigte Karten.

Luckie 14. Dez 2016 14:23

AW: Übungsprogramm "Kinokarten"
 
Ich habe es noch nicht angeguckt. :mrgreen:

Delphi-Quellcode:
procedure TForm1.CloseExecute(Sender: TObject);
begin
  Application.Terminate;
end;
Was ist denn das? Warum kein einfaches Close? Klar, kann ich beim Auto einfach den Zündschlüssel abziehen. Aber normalerweise bremst man, hält an, leg den ersten Gang ein, zieht die Handbremse, macht dann den Motor aus und zieht den Zündschlüssel ab.

Delphi-Quellcode:
procedure TForm1.EdRang1KeyPress(Sender: TObject; var Key: Char);
begin
   if not (Key in ['0'..'9', FormatSettings.DecimalSeparator, FormatSettings.ThousandSeparator, Char(VK_BACK)]) then
    Key := #0;
end;
Die Prozedur kommt neunmal vor und ist immer identisch. Einmal schreiben und allen Komponenten diese eine Ereignissprozedur zu weisen.

Delphi-Quellcode:
procedure TForm1.PreisExecute(Sender: TObject);
var anz : array[1..3] of integer;
    Rang : array[1..3] of integer;
    zwSu, Steuer, Endpreis : real;
begin
   try
      Anz[1]:=0;
      Anz[2]:=0;
      Anz[3]:=0;
      ZwSu:=0.0;
      Steuer:=0.0;
      Endpreis:=0.0;

//Eingabe der Kartenanzahl zum Kauf
      EdRang1.SetFocus;
      Anz[1]:=strtoint(EdRang1.Text);
      Rang[1]:=Anz[1]*15;
        if ckbxR1.Checked then Rang[1]:=Rang[1]-(Rang[1]div 10)
          else if not ckbxR1.checked then
            Rang[1]:=Anz[1]*15;
        if frKaR[1]<Anz[1] then
          begin
            Showmessage('Sie wollen mehr Karten 1. Rang verkaufen als Plätze vorhanden sind!');
             exit;
          end;

      Anz[2]:=strtoint(EdRang2.Text);
      Rang[2]:=Anz[2]*13;
        if ckbxR2.Checked then Rang[2]:=Rang[2]-(Rang[2]div 10)
          else if not ckbxR2.checked then
            Rang[2]:=Anz[2]*13;
        if frKaR[2]<Anz[2] then
          begin
            Showmessage('Sie wollen mehr Karten 2. Rang verkaufen als Plätze vorhanden sind!');
             exit;
          end;

      Anz[3]:=strtoint(EdRang3.Text);
      Rang[3]:=Anz[3]*10;
        if ckbxR3.Checked then Rang[3]:=Rang[3]-(Rang[3]div 10)
          else if not ckbxR3.checked then
            Rang[3]:=Anz[3]*10;
         if frKaR[3]<Anz[3] then
          begin
            Showmessage('Sie wollen mehr Karten 3. Rang verkaufen als Plätze vorhanden sind!');
             exit;
          end;

       if frPl<Anz[1]+Anz[2]+Anz[3] then
       begin
          Showmessage('Sie wollen mehr Karten verkaufen als Plätze vorhanden sind!');
           exit;
       end;
//Preisberechnung für erworbene Karten
      ZwSu:=ZwSu+Rang[1]+Rang[2]+Rang[3];
      Steuer:=ZwSu*0.19;
      Endpreis:=ZwSu+Steuer;
      AusgEndpreis.Text:=floattostrF(Endpreis, ffCurrency, 8,2);

//Ausgabe der Preisinformation
    MessageDlg('Der Preis '+floattostrF(Endpreis,ffCurrency, 8,2)+' setzt sich zusammen aus '+floattostrF(zwSu,ffCurrency, 8,2)+' Netto plus '#10#13+floattostrF(Steuer, ffCurrency, 8,2)+' MWSt' , mtInformation, [mbOK], 0);

//Berechnung der verkauften Karten und der freien Plätze
      KartR[1]:=KartR[1]+Anz[1];
      frKaR[1]:=FrKaR[1]-Anz[1];
      gek1Rang.Text:=inttostr(KartR[1]);
      frKaR1.Text:=inttostr(frKaR[1]);

      KartR[2]:=KartR[2]+Anz[2];
      frKaR[2]:=FrKaR[2]-Anz[2];
      gek2Rang.Text:=inttostr(KartR[2]);
      frKaR2.Text:=inttostr(frKaR[2]);

      KartR[3]:=KartR[3]+Anz[3];
      frKaR[3]:=FrKaR[3]-Anz[3];
      gek3Rang.Text:=inttostr(KartR[3]);
      frKaR3.Text:=inttostr(frKaR[3]);

      GesamtPl:=GesamtPl+ Anz[1]+Anz[2]+Anz[3];
      FrPl:=FrPl-(Anz[1]+Anz[2]+Anz[3]);
      AusgKartenGes.Text:=inttostr(GesamtPl);
      AusgFreiePl.Text:=inttostr(FrPl);

//Hinweise für verkaufte Karten
   if KartR[1]>20 then
    begin
      ShowMessage('Es wurden 20 Karten 1. Rang verkauft!');
    end;

   if KartR[2]>30 then
    begin
      ShowMessage('Es wurden 30 Karten 2. Rang verkauft!');
    end;

    if KartR[3]>50 then
    begin
      ShowMessage('Es wurden 50 Karten 3. Rang verkauft!');
    end;

//Gesamtplätze berechnen
   if GesamtPl>=100 then
     begin
       ShowMessage('Es sind bereits 100 Karten verkauft');
          EdRang1.Enabled:=false;
        EdRang2.Enabled:=False;
        EdRang3.Enabled:=False;
        Button1.Enabled:=false;
     end;
          zwSu:=0.0;
        Steuer:=0.0;
        Endpreis:=0.0;
  except
    on EConvertError do showMessage(Fehler);
  end;
end;
Ich habe mal den ganzen Code reinkopiert. 113 Zeilen. Für mich viel zu lang für ein Prozedur. Du hast es ja schon die Abschnitte kommentiert, was da gemacht wird:
Delphi-Quellcode:
//Preisberechnung für erworbene Karten
      ZwSu:=ZwSu+Rang[1]+Rang[2]+Rang[3];
      Steuer:=ZwSu*0.19;
      Endpreis:=ZwSu+Steuer;
      AusgEndpreis.Text:=floattostrF(Endpreis, ffCurrency, 8,2);

//Ausgabe der Preisinformation
    MessageDlg('Der Preis '+floattostrF(Endpreis,ffCurrency, 8,2)+' setzt sich zusammen aus '+floattostrF(zwSu,ffCurrency, 8,2)+' Netto plus '#10#13+floattostrF(Steuer, ffCurrency, 8,2)+' MWSt' , mtInformation, [mbOK], 0);

//Berechnung der verkauften Karten und der freien Plätze
      KartR[1]:=KartR[1]+Anz[1];
      frKaR[1]:=FrKaR[1]-Anz[1];
      gek1Rang.Text:=inttostr(KartR[1]);
      frKaR1.Text:=inttostr(frKaR[1]);

      KartR[2]:=KartR[2]+Anz[2];
      frKaR[2]:=FrKaR[2]-Anz[2];
      gek2Rang.Text:=inttostr(KartR[2]);
      frKaR2.Text:=inttostr(frKaR[2]);

      KartR[3]:=KartR[3]+Anz[3];
      frKaR[3]:=FrKaR[3]-Anz[3];
      gek3Rang.Text:=inttostr(KartR[3]);
      frKaR3.Text:=inttostr(frKaR[3]);

      GesamtPl:=GesamtPl+ Anz[1]+Anz[2]+Anz[3];
      FrPl:=FrPl-(Anz[1]+Anz[2]+Anz[3]);
      AusgKartenGes.Text:=inttostr(GesamtPl);
      AusgFreiePl.Text:=inttostr(FrPl);
Ich würde jeden Abschnitt in ein eigene Prozedur auslagern. Das macht den Code übersichtlicher und die Kommentare werden überflüssig. Zudem: Was für eine Exception erwartest du in dem Codeblock?

Und immer wieder die selben Meldungen:
Delphi-Quellcode:
//Hinweise für verkaufte Karten
   if KartR[1]>20 then
    begin
      ShowMessage('Es wurden 20 Karten 1. Rang verkauft!');
    end;

   if KartR[2]>30 then
    begin
      ShowMessage('Es wurden 30 Karten 2. Rang verkauft!');
    end;

    if KartR[3]>50 then
    begin
      ShowMessage('Es wurden 50 Karten 3. Rang verkauft!');
    end;
Besser:
Delphi-Quellcode:
TForm1.ShowSoldTickets(CntTickets: Integer; Rang: Integer)
begin
  ShowMessage(Format(..., [CntTickets, Rang]));
end;
Genauso mit den Fehlermeldungen.

Und wenn du das alle sin eien Klasse TKinokasse verpackst, hast du eine Trennung von Datenverarbeitung und Benutzeroberfläche, das ganze wird leichter erweiterbar und übersichtlicher, weil die ganzen Steuerelemnte im Code verschwinden.

Aviator 14. Dez 2016 14:26

AW: Übungsprogramm "Kinokarten"
 
Zitat:

Zitat von Luckie (Beitrag 1356120)
Was ist denn das? Warum kein einfaches Close? Klar, kann ich beim Auto einfach den Zündschlüssel abziehen. Aber normalerweise bremst man, hält an, leg den ersten Gang ein, zieht die Handbremse, macht dann den Motor aus und zieht den Zündschlüssel ab.

Du hast das Lenkradschloss vergessen. :mrgreen:

Luckie 14. Dez 2016 14:32

AW: Übungsprogramm "Kinokarten"
 
Zitat:

Zitat von Aviator (Beitrag 1356121)
Zitat:

Zitat von Luckie (Beitrag 1356120)
Was ist denn das? Warum kein einfaches Close? Klar, kann ich beim Auto einfach den Zündschlüssel abziehen. Aber normalerweise bremst man, hält an, leg den ersten Gang ein, zieht die Handbremse, macht dann den Motor aus und zieht den Zündschlüssel ab.

Du hast das Lenkradschloss vergessen. :mrgreen:

Das lasse ich vom Autodieb selber einrasten. :mrgreen:

EdAdvokat 14. Dez 2016 15:36

AW: Übungsprogramm "Kinokarten"
 
Ich will aus der Anweisung Application.terminate keine Ideologie machen, oder gar ein "Autounfall" provozieren. Bislang habe ich auch immer "close" für das Schließen des Programmes benutzt und mußte u.a. auch hier in der DP und in anderen Foren lernen, dass "close" lediglich zum Schließen eines Formulars eingesetzt werden sollte, jedoch nicht zum Schließen des Programms selbst. Dies wurde wie eine Todsünde behandelt. Nun kommt die Rolle rückwärts? Da habe ich doch brav all die Hinweise der Foren berücksichtigt und soll nun also wieder vom Glauben abfallen? Ist es denn so schlimm mit Application.terminate?
Über die anderen Fakten werde ich nachdenken.
Wie ich die Exception besser lösen könnte ist mir nicht klar. Ich weis da nicht so richtig weiter. Einfach nur frei lassen (also den Quatsch mit "on EConvertError do showMessage(Fehler)" weglassen?) und erwarten was für eine Exception-Meldung kommt oder was? Da könnte ich eine Anregung gebrauchen. Danke

haentschman 14. Dez 2016 15:47

AW: Übungsprogramm "Kinokarten"
 
Moin...8-)
Zitat:

und mußte u.a. auch hier in der DP und in anderen Foren lernen, dass "close" lediglich zum Schließen eines Formulars eingesetzt werden sollte
Das hast du nicht von hier... :zwinker: Auch deine Anwendung ist auch ein Formular. :P ...allerdings das Letzte.
Mit Terminate brichst du das Programm HART ab. (sinngemäß) Beispiel: Datenbanken werden u.u. nicht richtig geschlossen. Das kann zu Problemen führen.

..also immer CLOSE :thumb:

Luckie 14. Dez 2016 16:11

AW: Übungsprogramm "Kinokarten"
 
Wenn du die eine große Prozedur, wie schon vorgeschlagen, in einzelne zerlegst, kannst du gezielt mögliche Exceptions abfangen.

Und zum Close: Ein Aufruf von Close im Hauptformular bewirkt genau das, dass die Anwendung beendet wird. Und bitte zeig mir den Thread hier im Forum, wo gesagt wird, dass Apüplication.Terminate zum Beendne einer Anwendung vor zu ziehen ist. Klar auch wenn man eine Anwendung/Prozess hart beendet gibt Windows alle Systemressourcen frei, DLls werden entladen, alle offenen Handles werden geschlossen usw. Aber Close ist einfach sauberer.

@haentschman: Sockets und DBs sollte man wohl extra behandeln zu Sicherheit, sprich explizit selber schließen, da würde ich mich nicht auf Windows verlassen.

EdAdvokat 15. Dez 2016 13:57

AW: Übungsprogramm "Kinokarten"
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich gebe zu, mit den Hinweisen war ich gefordert und das Ergebnis (Datei anbei) ist sicher nicht so, wie erwaret. Ich komme nicht weiter mit einer effektiveren Verkleinerung der großen Prozedur "Kartenbestellung". Bei jeder versuchten Neuaufteilung kommt die Zählerei der noch freien Plätze durcheinander. Wenn diese Berechnung ordentlich durchführt wird, gibt er trotz nicht freier Kinoplätze eine Rechnungssumme aus. Ich habe mehrere Varianten ausprobiert, doch meine bescheidenen Kenntnisse...
In der jetzigen Form sollte alles so wie vorgesehen funktionieren.
Noch eine kleine Frage habe ich zum Compilerhinweis besser charlnset zu verwenden bei der Abfrage der Editfelder (Key in...)

Luckie 15. Dez 2016 14:56

AW: Übungsprogramm "Kinokarten"
 
Deswegen das ganze in eine Klasse kapseln.

Aviator 15. Dez 2016 16:43

AW: Übungsprogramm "Kinokarten"
 
Zitat:

Zitat von EdAdvokat (Beitrag 1356242)
Noch eine kleine Frage habe ich zum Compilerhinweis besser charlnset zu verwenden bei der Abfrage der Editfelder (Key in...)

Und wie wäre die Frage?

CharInSet solltest du bei neueren Delphi Versionen verwenden. Müsste dann so aussehen:
Delphi-Quellcode:
if not CharInSet(Key, ['0'..'9']) then

EdAdvokat 15. Dez 2016 19:50

AW: Übungsprogramm "Kinokarten"
 
Toll, Danke und es funktioniert sogar!!! Wieder ein kleines Problem gelöst. Ich finde es toll, dass das geballte Wissen so nieder regnet und wir es aufsaugen können (soll keinesfalls falsch aufgefasst werden!). Ich sitze hier in meinem Kämmerlein und grüble so nach und freue mich über jede neue Anregung und nehme sie auf, um daraufhin wieder neu zu starten (oft stundenlang, denn ich habe als Rentner durchaus Zeit oder nehme sie mir), was oft nicht ganz einfach ist, denn so gut bin ich noch nicht, doch es macht mir viel Spaß.
Nun stehe ich vor dem großen Problem von Luckie, der mir geraten hat die ganze Sache in eine Klasse zu verpacken. Richtig! aber wie fange ich das an? Erst einmal will ich die ganze Sache neu und ganz einfach schreiben. (OOP!!!) Oh je!. ist sicher richtig und zukunftsweisend. Doch es ist nicht einfach dieser Denkweise Raum zu geben.
Ich habe zurückliegend einem Projekt der Handelsschule in Berlin folgend ein OOP-Programm für mich geschrieben und will dieser Richtlinie nun folgen. Mal sehen ob was raus kommt. Jedenfalls habe ich in den letzten Tagen durch die Dialoge mit Euch einiges gelernt!!!

EdAdvokat 18. Dez 2016 13:18

AW: Übungsprogramm "Kinokarten"
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo zusammen,
bevor ich in dieser Angelegenheit weiter fortfahre, möchte ich mich erst einmal vergewissern, ob ich auf dem richtigen Weg bin. Ich habe also versucht das bisherige Projekt Kinokarten (prozedural) nun in OOP zu schreiben. Ob es mir annähernd gelungen ist, bleibt abzuwarten und ich bitte daher um wohlwollende Kritik, damit ich meinen neuen Kurs neu justieren kann. Nach meinen Prüfungen dürfte all das was ich hier angeboten habe funktionieren. Auch die Sicherung der Eingabefelder(edits) habe ich etwas anders als bislang realisiert (NumberOnly, ReadOnly). All meine Prüfversuche, Blödsin in diese Felder zu kopieren klappt nicht.
Sollte ich als richtig liegen mit den vorangegangenen Hinweisen, könnte ich also noch den 3. Rang und die Begrenzung der jeweils zu verkaufenden Karten und auch eine Ermäßigung einbauen.

Luckie 18. Dez 2016 18:45

AW: Übungsprogramm "Kinokarten"
 
Das kannst du alles private machen und verstecken. Öffentlich brauchst du nur drei Methoden:
Delphi-Quellcode:
BuyTickst(Anzahl: Integer; Rang: Integer);
Delphi-Quellcode:
RemainingTickts(Rang: Integer): Integer);
Delphi-Quellcode:
MaxTickets(Rang: Integer): Integer;
Mehr braucht der Nutzer deiner Klasse nicht wissen. Wie du das alles intern löst, interessiert nicht.

Aus Rang könnte man noch den Datentyp Enum machen. Ist aussagekräftiger.

Luckie 18. Dez 2016 19:05

AW: Übungsprogramm "Kinokarten"
 
Nachtrag: Um die oben genannten öffentlichen Methoden würde ich die Klasse aufbauen. Einfach gucken, was brauche ich intern, um das umzusetzen. Welche Setter? Welche Getter? Wo und wie berechne ich was.

Man könnte aus der Karte auch ein Record machen, ist vielleicht noch besser. Ein Record mit folgenden Feldern: Rang (vom Typ Enum) und Preis.

Und für den Preis nimm besser den Datentyp Currency. Der ist genau für so was gemacht.

EdAdvokat 18. Dez 2016 19:56

AW: Übungsprogramm "Kinokarten"
 
Hallo Luckie, ich kann wirklich nicht genau erkennen, welche meiner Methoden ich nur public lassen und die anderen privat deklarieren sollte. Könntest Du mir da anhand meiner Methoden einen konkreteren Tipp geben.(Welche meiner deklarieren Methoden meinst Du?) Was würde es schaden, wenn ich sie alle public lasse? Was hätte dass für Auswirkungen?
Grundsätzlich eine Frage: bin ich mit der OOP-Variante auf dem richtigen Weg?
Welchen Vorteil habe ich mit der Verwendung von enium?

Luckie 18. Dez 2016 20:09

AW: Übungsprogramm "Kinokarten"
 
Wie schon gesagt. So ziemlich alle. Und ich würde von außen an die Sache ran fegen und nicht von innen. Welche Methoden braucht der Nutzer deiner Klasse?

Warum nicht alle public? Weil es zum einem übersichtlicher ist und er nicht in Versuchung kommt interne Methoden zu verwenden, deren Schnittstelle sich ändert. Bei den public Methoen kann er sich sicher sein, dass sich die Schnittstelle nicht ändert, zumindest, wenn sich auch der Ersteller der Klasse dran hält. Es kann ja sein, dass du die Klasse erweiterst und sich Parameter von Methoden ändern. Bei private Methoden kein Problem. Bei public Methoden dürftest du dir den Zorn des Nutzers zu ziehen.

Warum Enum? Was ist einfacher zu verstehen? Ein Integer, bei dem keiner weiß, was er bedeutet oder ein aussagekräftiger Enum Typ?
Delphi-Quellcode:
type
   TTicket = (Rang1, Rang2, Rang3);
Und wenn du es erweitern willst:
Delphi-Quellcode:
type
   TTicket = (Rang1, Rang2, Rang3, Loge, Parkett);

Luckie 18. Dez 2016 20:40

AW: Übungsprogramm "Kinokarten"
 
Ein Problem ist auch die Verwaltung der noch verbleibenden Karten. Man könnte überlegen, ob man eine extra Klasse TTickt macht mit: Typ, Preis, MaxAnzahl und Rest oder so.
Und dann ein Array bzw. Objektliste für die Kartentypen.

Das ist schon nicht ganz banal, das elegant zu lösen. Das erfordert schon etwas überlegen. Mal sehen, wie ich morgen Zeit und Lust habe.

Luckie 18. Dez 2016 20:58

AW: ÜbungsproUnd wie mann siehtgramm "Kinokarten"
 
Und wie man sieht, es lohnt sich etwas nachzudenken bevor man los programmiert. Bisher habe ich noch keine Zeile programmiert, aber man sieht, dass sich so langsam eine recht gute Lösung abzeichnet.

(Nur mal so als Anmerkung für die Anfänger, für die ja dieses Demoprogramm gedacht ist.)

Jumpy 19. Dez 2016 08:17

AW: Übungsprogramm "Kinokarten"
 
Delphi-Quellcode:
BuyTickst(Anzahl: Integer; Rang: Integer);
Hallo,

hab mir dein Beispiel nicht angeguckt und reagiere einfach nur mal auf die Prozedure, die Lucky hier zitiert hat. Um das OOP-mäßig weiter auszubauen, könnte man vllt. wie folgt vorgehen:

Delphi-Quellcode:
BuyTickst(Warenkorb:TWarenkorb);
Sprich sowas wie ein Warenkorb-Objekt, das wiederum die zu kaufenden Tickets enthält. Ob das nun jedes Ticket einzeln ist (würde ab irgendeinem Punkt sicher Sinn machen, wenn es nummerierte Platztickets gibt) oder Listen aus Ticketanzahl pro Rang.

Luckie 19. Dez 2016 14:07

AW: Übungsprogramm "Kinokarten"
 
Das wäre eine gute Idee, wenn man Karten unterschiedlicher Preisklassen auf einmal verkaufen will. Bei dem Projekt ist noch viel Luft nach oben. ;)

Der schöne Günther 19. Dez 2016 14:13

AW: Übungsprogramm "Kinokarten"
 
Wenn es in dem Tempo weitergeht: Noch zwei Monate und es ist weltweiter Marktführer :wink:

p80286 19. Dez 2016 15:25

AW: Übungsprogramm "Kinokarten"
 
Zitat:

Zitat von Jumpy (Beitrag 1356543)
Delphi-Quellcode:
BuyTickst(Anzahl: Integer; Rang: Integer);
Hallo,

hab mir dein Beispiel nicht angeguckt und reagiere einfach nur mal auf die Prozedure, die Lucky hier zitiert hat. Um das OOP-mäßig weiter auszubauen, könnte man vllt. wie folgt vorgehen:

Delphi-Quellcode:
BuyTickst(Warenkorb:TWarenkorb);
Sprich sowas wie ein Warenkorb-Objekt, das wiederum die zu kaufenden Tickets enthält. Ob das nun jedes Ticket einzeln ist (würde ab irgendeinem Punkt sicher Sinn machen, wenn es nummerierte Platztickets gibt) oder Listen aus Ticketanzahl pro Rang.

Den Warenkorb finde ich etwas unglücklich, da nur Tickets verkauft werden sollen. fall noch Popkorn dazu käme, könnte man über TWarenkorb=TTickets+TPopcorn einmal nachdenken.

Gruß
K-H

nahpets 19. Dez 2016 15:42

AW: Übungsprogramm "Kinokarten"
 
Naja, sagen wir mal so: TWarenkorb ist ein Container, der die Tickets für heute, die Geschenkgutscheine für Kinokarten, die Eiscreme nebst Popcorn ... enthalten kann.

Wenn die Ideen weiter sprudeln, dann wird das Übungsprogramm "Kinokarten" in kürzester Zeit beliebig komplex.

p80286 19. Dez 2016 16:00

AW: Übungsprogramm "Kinokarten"
 
Zitat:

Zitat von nahpets (Beitrag 1356603)
Naja, sagen wir mal so: TWarenkorb ist ein Container, der die Tickets für heute, die Geschenkgutscheine für Kinokarten, die Eiscreme nebst Popcorn ... enthalten kann.

Wenn die Ideen weiter sprudeln, dann wird das Übungsprogramm "Kinokarten" in kürzester Zeit beliebig komplex.

Jo, und da die Beteiligten meist wissen wovon sie schreiben, hätten wir dann einmal OOP abseits praxisnaher Beispiele, wie z.B. "Fahrzeuge".

Gruß
K-H

EdAdvokat 19. Dez 2016 16:01

AW: Übungsprogramm "Kinokarten"
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo zusammen, wollen wir doch erst einmal ein Dorfkino bauen und später dann ein Filmpalast für Hollywood (das überlasse ich jedoch den wirklichen Könnern.
Ich bin mit meinen bescheidenen Kenntnissen doch etwas gefordert und muss hier und da doch mal in die Bücher/Internet sehen, um die Anregungen umzusetzen. Zunächst ein weiterer Zwischenschritt, der noch weiter verbessert wird. (habe mit const gearbeitet und werde jetzt beginnen wie Luckie empfohlen hat den Rang als Enum umzuwandeln und dann von "außen zu fegen" beginnen - sprich die Teilung in public und private.
Ziel ist es ein wirklich lauffähiges und dann ausbaufähiges Programm vorzulegen. Schauen wir mal, dann sehen wir....

nahpets 19. Dez 2016 16:49

AW: Übungsprogramm "Kinokarten"
 
Bin so frei und meckere ein bisserl rum ;-)

Dashier gefällt mir nicht:
Delphi-Quellcode:
procedure TKinokarten.BerechnePreisR1; //Preis R1 berechnen
begin
    if Form1.ckbxR1.Checked then PreisR[1]:=Karten[1] * Rang1erm
          else if not Form1.ckbxR1.checked then
       PreisR[1]:= Karten[1] * Rang1;
end;
Hier ist es zwingend erforderlich, dass die Klasse TKinokarten irgendwie in einem Form1 liegt. Nutzt jemand sie in 'nem Formular fmKino, hat er ein unlösbares Problem.

Lieber sowas in der Art:
Delphi-Quellcode:
procedure TKinokarten.BerechnePreisR1(aErmaessigt : Boolean); //Preis R1 berechnen
begin
  case aErmaessigt of
    true : PreisR[1] := Karten[1] * Rang1erm;
    false : PreisR[1] := Karten[1] * Rang1;
  end;
end;
Oder auch sowas:
Delphi-Quellcode:
procedure TKinokarten.BerechnePreisR1(aErmaessigt : Boolean); //Preis R1 berechnen
begin
  PreisR[1] := IfThen(aErmaessigt,Karten[1] * Rang1erm,Karten[1] * Rang1);
end;
Der Aufruf im Programm sähe dann so aus:
Delphi-Quellcode:
Kinokarten.BerechnePreisR1(ckbxR1.checked);


Alles, was zur Klasse TKinokarten gehört, würd' ich in eine Unit Kinokarten auslagern und die dann in die Uses-Anweisung aufnehmen. Dann fallen so unglückliche Zugriffe auf Form1 innerhalb der Klasse sofort auf und man merkt, dass man hier eine ungünstige Konstruktion gewählt hat.

Dashier finde ich schön:

Delphi-Quellcode:
showMessage(format('Es wurden %d Karten im %d. Rang für '+inttostr(Rang1)+' Euro bzw. für ermäßigt '+inttostr(Rang1erm)+' Euro verkauft!',[20,1]));


Ein bisserl Format und ein bisserl auch nicht. Lieber sowas:

Delphi-Quellcode:
showMessage(format('Es wurden %d Karten im %d. Rang für %d Euro bzw. für ermäßigt %d Euro verkauft!',[20,1,Rang1,Rang1erm]));


Wobei: Für die 20 und die 1 (und alle ähnlichen Werte irgendwo im Quelltext) würd' ich zu den bereits vorhandenen Konstanten noch ein paar entsprechende dazu packen.
Wenn denn dann mal was geändert werden muss, dann hat man das alles an einer Stelle. Später könnte man dann noch überlegen, ob man die Werte der Konstanten nicht in der Prozedure Init aus 'ner Ini-Datei (oder Ähnlichem) liest. Dann kann man die Preise und das Platzangebot ändern, ohne das Programm ändern zu müssen.
Dazu gönnt man dann der Klasse entsprechende Attribute, statt auf globale Konstanten zuzugreifen.

EdAdvokat 19. Dez 2016 17:27

AW: Übungsprogramm "Kinokarten"
 
Danke nahpets, sehe ich mir sofort an - gefällt mir gut.
Habe empfehlungsgemäß ein Enum gebildet:
...
implementation

{$R *.dfm}

uses ABOUT;

type TRang = (Rang1=15, Rang1erm=14, Rang2=13, Rang2erm=12, Rang3=10, Rang3erm=9);
var Rang: TRang;

const {Rang1 = 15;
Rang1erm = 14;
Rang2 = 13;
Rang2erm = 12;
Rang3 = 10;
Rang3erm = 9; }
PlGes = 100;
frPlR1Vorg = 20;
frPlR2Vorg = 30;
frPlR3Vorg = 50;...

nun will ich mit dem Enumtyp eine Multiplikation ausführen und den Preis für Rang 1 zu berechnen, doch es wird
eine Fehlermeldung ausgegeben:
[dcc32 Fehler] uKinokarten.pas(227): E2015 Operator ist auf diesen Operandentyp nicht anwendbar

procedure TKinokarten.BerechnePreisR1; //Preis R1 berechnen
begin
if Form1.ckbxR1.Checked then PreisR[1]:=Karten[1] * Rang1erm
else if not Form1.ckbxR1.checked then
PreisR[1]:= Karten[1] * Rang[1]; //Rang1;
end;

Was mache ich falsch? Gehe ich mit Rang[2] ins Rennen, also mit dem Ordinalwert meckert er auch und will ein
array deklariert sehen, doch das ist ja nicht vorgesehen.
Mit den const-Werten hat alles geklappt!

nahpets 19. Dez 2016 18:04

AW: Übungsprogramm "Kinokarten"
 
Hier könnte man eventuell sowas machen: (Achtung, nur hingedaddelt und nicht getestet)
Delphi-Quellcode:
type
  TRang = (Rang1=15, Rang1erm=14, Rang2=13, Rang2erm=12, Rang3=10, Rang3erm=9);

  TKinokarten = class
    private
      Karten         : array[TRang] of integer;
      gekaufteKarten : array[TRang] of integer;
      freiePlR       : array[TRang] of integer;
      gekaufteKartenGesamt,
      freiePlGesamt : integer;

      PreisR       : array[TRang] of Currency;
      ZwSu         : Currency;
      MWSt         : Currency;
      Preis        : Currency;
...

// Klappt so mit einer Prozedur für alle Ränge.
procedure TKinokarten.BerechnePreisFuerBeliebigenRang(aRang : TRang);
begin
  PreisR[aRang] := Karten[aRang] * Ord(aRang);
end;
Bekommt man jetzt weitere Ränge dazu oder irgendwelche weiteren Ermäßigungen ..., so muss man nur den Typ tRang erweitern, der Rest läuft automatisch.

EdAdvokat 19. Dez 2016 20:03

AW: Übungsprogramm "Kinokarten"
 
Danke Stefan, das hat geklappt. Nun muss ich nur noch die Trennung TForm1 und TKinokarten vollziehen.
Da muss ich mir noch was einfallen lassen. Nochmals vielen Dank!


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:35 Uhr.
Seite 1 von 2  1 2      

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