====== LU07.A06 - Unpure wird pure ====== Entfernen Sie den globalen Zustand aus einer kleinen Lagerverwaltung und trennen Sie Berechnung von Ein- und Ausgabe. ==== Ausgangslage ==== lager = {} protokoll = [] def einlagern(artikel, menge): global lager if artikel in lager: lager[artikel] += menge else: lager[artikel] = menge protokoll.append(f"+{menge} {artikel}") print(f"{artikel}: {lager[artikel]}") def entnehmen(artikel, menge): global lager if lager.get(artikel, 0) < menge: print("zu wenig Bestand") return False lager[artikel] -= menge protokoll.append(f"-{menge} {artikel}") return True einlagern("maus", 5) einlagern("maus", 3) entnehmen("maus", 2) # True entnehmen("maus", 99) # False print(lager) # {'maus': 6} ==== Vorgehen ==== Führen Sie zuerst ''pytest'' aus. Sieben Tests sind **rot** - das ist Absicht. Die Tests sind hier die Spezifikation. Zwei davon lohnen sich genauer anzuschauen: * ''test_lauf_ist_wiederholbar'' - zwei Durchläufe liefern verschiedene Ergebnisse, weil der globale Zustand zwischen den Aufrufen hängen bleibt. * ''test_lauf_ausgabe'' - schlägt fehl, sobald vorher ein anderer Test gelaufen ist. Genau das meint «Tests hängen voneinander ab». ==== Zu implementieren ==== mit_einlagerung(bestand, artikel, menge) -> dict mit_entnahme(bestand, artikel, menge) -> (dict, bool) Beide Funktionen sind **pure**: * Sie verändern ''bestand'' nicht, sondern geben einen neuen zurück. * Sie geben nichts auf der Konsole aus. * Derselbe Aufruf liefert immer dasselbe Ergebnis. ''lauf()'' ist die Schale. Dort - und nur dort - leben Zustand, Protokoll und ''print''. Am Schluss existieren die globalen Variablen ''lager'' und ''protokoll'' nicht mehr; ''einlagern'' und ''entnehmen'' in ihrer alten Form auch nicht. ==== Regeln ==== * ''main_test.py'' wird **nicht** verändert. * Rückgabewerte und Ausgabe von ''lauf()'' bleiben exakt gleich. * Kein ''global'' im fertigen Code. ==== Bewertung ==== ^ Teil ^ Punkte ^ | Tests (''main_test.py'') | 14 | | pylint (''main.py'') | 5 | ==== Leitfragen für die Reflexion ==== * Wie gibt eine Funktion gleichzeitig den neuen Bestand und die Information «hat geklappt / hat nicht geklappt» zurück? * Wohin gehört das Protokoll - in den Kern oder in die Schale? * Was wird an dieser Version einfacher zu testen als vorher? ---- https://github.com/templates-python/m323-lu07-a06-purerefactoring {{tag>M323-LU07 M323-D1I}} [[https://creativecommons.org/licenses/by-nc-sa/4.0/ch/|{{https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png}}]] (c) Kevin Maurizi