Nach den Klassen GELD, FACH und KASSE fehlt zum Schluss noch die Implementierung der Klasse KASSIERER. Diese Klasse soll den Vorgang des Kassierens abbilden, d.h. den Kundenkontakt, das Aufnehmen der Preise, das Entgegennehmen des gegebenen Geldes, das Berechnen des Wechselgeldes und das Zurückgeben von Restgeld. Idealerweise würde der Gesamtprozess ablaufen wie unten dargestellt.
Ablaufplan
Fertiges Programm
Grundgedanke des fertigen Programms ist es, die Eingaben und Ausgaben über die Konsole zu simulieren. Es wäre ein weitergehendes Projekt, wen man die Eingabe mit einem Warenscanner und die Ausgabe über kleines Display darstellen wollte. Die beiden Schleifen im Ablauf, also die Wiederholung eines Vorgangs für jeden Kunden und die Wiederholung für jede Ware, die bereits Teil der Vorüberlegungen zur Modellierung waren, werden hier durch den wiederholten Aufruf von Methoden umgesetzt. Das gilt für start() und band().
Eine Kasse besteht neben der Eingabe über Scanner oder Tasten und der Ausgabe in Form eines Displays aus einer Recheneinheit, und verschiedenen Geldfächern. Ein- und Ausgabe sollen zunächst vernachlässigt werden, weil sie Teil der späteren Benutzerschnittstelle sind. Damit bleibt zum einen als Eigenschaft der Kasse bestehen, dass sie verschiedene Geldfächer enthält. Zum anderen muss die Kasse verschiedene Rechenoperationen ausführen können, um dem Kassierer die Arbeit zu erleichtern. Die Kasse sollte:
Beträge summieren können,
die Bezahlung einlagern,
das nötige Wechselgeld errechnen können,
prüfen können, ob genug Wechselgeld vorhanden ist, und
das Wechselgeld bereitstellen.
Jeder der Forderungen steht eine Fähigkeit, also eine Methode der Klasse KASSE gegenüber:
A) Der Testfall
In einem Testfall soll die Kasse mit einer Menge an Wechselgeld gestartet werden. Anschließend werden Werte summiert und der Gesamtwert überprüft. Die Menge an Geld eines Faches wird abgefragt, bevor man einen bestimmten Geldbetrag einlagert; danach wird die Menge im Fach nochmals überprüft. Die Funktion test() müsste dann aufgerufen mit einer größeren Anzahl als Geldeinheiten im Fach sind False ergeben und bei einer geringeren Anzahl True. Schließlich sollte bereitstellen() die richtige Stückelung an Geldeinheiten zu einem Kommabetrag enthalten und die Menge der Geldeinheiten in einem bestimmten Fach um die richtige Anzahl reduzieren.
B) Grundlagen der nötigen Rechenoperationen
Die Methoden summiere(float betrag) und wechselgeld(float betrag) bestehen aus einer Addition bzw. einer Subtraktion von Kommawerten. Die Methoden einlagern(float betrag), test_wechsel(float betrag) und bereitstellen(float betrag) beruhen dagegen auf dem Berechnen der Anzahl von Geldeinheiten, die in einem Kommabetrag enthalten sind. So muss etwa bereitstellen(float betrag) abbilden, aus wie vielen unterschiedlichen Geldenheiten sich der Kommawert zusammensetzt.
Ganzzahlige Division int(a/b)
Wenn man mit Geldeinheiten rechnet, muss das Ergebnis einer Division immer ganzzahlig sein, weil eine Einheit entweder noch enthalten ist oder eben nicht mehr. So ist in 2,20 € genau ein 2 € Stück enthalten, aber nicht 2,2 Eurostücke. Das Ergebnis der Divison 2,20€ / 2€ muss also ein ganzzahliges Ergebnis liefern, also int(2,20/2).
Modulo (%)
Der Restbetrag der ganzzahligen Division ergibt sich am einfachsten durch eine Modulo-Operation. So ergeben 2,20 modulo 2 ebenso wie 4,20 % 2 die gewünschten 0,20, also genau den Restbetrag, der übrig bleibt, wenn man die nötige Anzahl an 2 Euromünzen bereits in Empfang genommen oder herausgegeben hat.
C) Berechung am Beispiel von test_wechsel(float betrag)
Der folgende Programmablaufplan stellt die nötigen Arbeitsschritte zum Überprüfen der Menge an Wechselgeld und ihren Zusammenhang dar.
def test(self, betrag):
# Wissen: return beendet die Ausführung einer Funktion
# für jedes Geldfach tue
for f in self.ReiheDerFaecher:
# benötigte Stückzahl =
# ganzzahlige Division von
# Betrag durch Wert des Geldes im Fach
menge = int(betrag / f.geld.get_wert())
# Überprüfe ob die Sückzahl im Fach ist:
# Nein? Gib zurück False
if not f.test(menge): return False
# Neuer Betrag = Restbetrag
# <= Betrag modulo Geldwert
betrag = round(betrag % f.geld.get_wert(), 2)
return True
# Alle Fächer durchlaufen ohne Abbruch?
# Gib zurück True
Aufgabe:
Programmiere die fehlenden Methoden aus, so dass das Skript fehlerfrei abläuft und richtig funktioniert.
Eine Implementierung des Modells nach dem Ansatz „Bottomup“ beginnt beim letzten Teil der von einander abhängigen Klassen bzw. Objekte, der Klasse GELD, und geht dann weiter zur Klasse FACH. Nachbesserungen sind denkbar.
a) Daten
Die grundlegenden Daten der Kassensimulation bestehen aus Geldmengen, also aus einer Zusammensetzung von verschiedenartigen Münzen und Scheinen. Die Eigenschaften von GELD sind ein Name (das Nominal), z.B. „1ct“, und ein entsprechender Wert wie 0.01. Der Wert drückt einen Bruchteil der angenommenen Einheit 1 € aus. Das bietet sich an, denn damit ist ein Umrechnungsfaktor unter den einzelnen Geldstücken gegeben. Je nach Programmiersprache benötigt die Klasse GELD keine besonderen Methode; im Beispiel wurden zur Demonstration zwei so genannte „Getter“ eingefügt.
b) Datenstruktur und grundlegende Methoden
Das Fach strukturiert die Geldmengen. Wenn also Geldmengen in der Situation die Daten sind, dann ist das Fach eine Datenstruktur. Jedes Fach der Kasse enthält eine gewisse Menge gleichartigen Geldes, also z.B. 35 der 1ct Münzen. Die Klasse FACH benötigt vier Methoden. Die Methode einlegen( anzahl int ) erhöht die Menge an Geld im Fach; die Methode herausgeben( anzahl int ) reduziert die Menge. Die Methode summe() -> float gibt den aktuellen Betrag aller im Fach enthaltenen Münzen zurück. Die Methode test( menge int ) -> boolean überprüft, ob die Anzahl der enthaltenen Geldstücke oder Geldscheine ausreicht, um eine bestimmte Menge zurückzugeben.
c) Komposition
Die Methoden der Klasse Fach lassen sich mit den Methoden einer Liste vergleichen. Die Enthält-Beziehung kann man durch zwei Attribute – Menge und Geld – oder über eine Liste der Geldeinheiten implementieren. Die Liste bietet sich an, wenn man den Geldeinheiten eine ID geben möchte, ansonsten reicht auch ein Objekt der Klasse GELD und die Angabe der Menge, wie viele Objekte davon im Fach liegen. Diese Eigenschaft rückt GELD in die Nähe von Bitcoin und ähnlichen virtuellen Währungssystemen.
Aufgabe
Die Methoden einlegen() und summe() der Klasse FACH sind bereits fertig implementiert, programmiere in ähnlicher Weise die Methoden herausgeben() und test(). Beachte, dass test() im Unterscheid zu herausgeben() einen Rückgabewert besitzt.
Schreibe ein geeignetes Testprogramm, das überprüft, ob die Klasse FACH funktioniert.
Überlege: Welche Möglichkeiten gäbe es, eine eindeutige ID für jede Geldeinheit zu erzeugen?
Veranschaulichung von FACH.summe()
Die grauen breiten Linien im Hintergrund stellen Teile des Skripts dar. So verweist die Linie FACH.summe() auf den Ort im Skript, wo diese Methode definiert ist und die nötigen Anweisungen stehen, also auf die Zeilen 35 bis 38. Die roten und gelben Pfeile geben die Abfolge der Ausführung wieder. Der Sprung geld.get_wert() geschieht zur Laufzeit in Zeile 37. Sobald der erforderliche Wert da ist, also zurückgegeben wurde, kann die Summe berechnet und dieser Betrag seinerseits zurückgegeben werden. Vgl. dazu Zeile 38.
Die Modellierung des Kassensystems beginnt mit der Anschauung und Beschreibung des Problemfeldes.
Kassierer und Kassierinnen am Warenband scannen die Preise der Waren eines Einkaufs. Wenn die Eingabe abgeschlossen ist, nennen sie den Gesamtpreis und fordern sie die Kundschaft zum Bezahlen auf. Sie nehmen das Geld in Empfang, öffnen die Kasse und sortieren die Geldeinheiten in die richtigen Fächer. Bei Überbezahlung wird unmittelbar anschließend aus denselben Fächern das Wechselgeld entnommen und der Kundschaft zurückgegeben. Dann folgt der nächste Kunde.
Der Ablauf beim Kassieren wird durch das durchlaufende Warenband vorgegeben:
In einem äußeren Kreislauf wechseln die Kunden; jeder neue Kunde wird nach derselben Routine bedient.
In einem inneren Kreislauf werden zu jedem Kunden die Waren auf dem Band erfasst und es wird der Gesamtpreis errechnet.
Die Situation an der Kasse kann man daher in vier Bestandteile auflösen: Den Kassierer, die Kasse, das (Geld-)Fach und das Geld. Von der Ware interessiert den Kassierer eigentlich nur der Preis.
Zur objektorientierten Implementierung des Modells sind die Klassen KASSIERER, KASSE, FACH und GELD vorstellbar. Vgl. Abbildung 4. Die Abbildung veranschaulicht, wie jede Klasse mindestens ein Objekt der folgenden Klasse enthält.
Projektziele
Wenn die folgenden Ziele erreicht sind, soll die Kassensituation als realistisch abgebildet gelten und das Ende des Projekts ist erreicht.
Der digitale Kassierer verfügt über eine Menge an Geldstücken und Geldscheinen, etwa 10 x 1Cent, 10 x 2 Cent usw.
Eingegebene Warenpreise werden summieren.
Nach Abschluss der Summenbildung wird um die Eingabe eines möglichst passenden Geldbetrags gebeten und das nötige Wechselgeld errechnet.
Wenn das Kleingeld in der Kasse nicht für das Wechselgeld ausreicht, wird der Kunde aufgefordert mit der Karte zu zahlen.
Optional: Ist es möglich, eine digitale Währung mit einer Art Seriennummer einzuführen?
„Puh!“, ächzt der Mann hinter dem Tisch am Ladenausgang, „Ständig diese Rechnerei mit der Preisen! Und dann immer wieder das Wechselgeld: Wenn es nicht schon Registrierkassen mit Warenscanner gäbe, müsste man die glatt erfinden.“ Naja. Das ist doch kein Problem. Sie wünschen, wir erfinden: Herzlich willkommen zum Fall „Kassierer“.
Die Registrierkasse wurde gegen Ende des 19. Jahrhunderts erfunden. Zentrale Idee war es ursprünglich, dass sich die Schublade mit den Geldfächern nur beim Kassieren der Waren öffnete und dadurch den Diebstahl von Kleingeld verhinderte oder zumindest erschwerte. Seit den Anfängen haben Elektronik und digitale Technik die Kassen sehr verändert. Sie dienen nun nicht mehr nur der Aufbewahrung von Geld, sondern können auch jeden Einkauf, also jeden Geschäftsabschluss dokumentieren. Daher haben Kassensysteme heute eine zentrale Funktion in der Gastronomie oder im Einzelhandel auch im Hinblick auf die Umsatzsteuer und es gibt eigens formulierte Grundsätze des Bundesministeriums für Finanzen, wie die dabei entstehenden Daten zu behandeln sind.
Zu Übungszwecken soll hier ein Kassensystem nachprogrammiert werden. Die Arbeitsweise im Projekt beginnt mit der Modellierung und wendet sich dann bottomup der Erstellung der Programmskripte zu. Der Fall „Kassierer“ besteht damit aus vier Teilen: