Guter Programmierstil

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

Guten Morgen,

zuallererst: Das ganze ist keine Hausaufgabe, sondern reine Hobbysache.

Ich möchte ein kleines Kassensystem programmieren, um mich im Programmieren etwas "warm" zu halten.

Nun habe ich jedoch meine Probleme mit der Programmlogik.

Ich habe mir diverse Klassen überlegt, diese werden alle in der Mysql-Datenbank gespeichert werden (also deren Daten).

Konkret überlegt habe ich mir:

[*]Produktdefintion
[*]Lager
[*]Kunde
[*]Transaktion (Verkaufsvorgang)


Stimmt das soweit von der Logik? Würdet ihr das etwas anders machen?

Probleme/Fragen die sich mir nun ergeben sind folgende:

[*]Wie fasst man Produkte am intelligentesten in Menge zusammen? Ich möchte ja in der GUI dann z.B. ein Mengenfeld haben, wieviele Produkte also im Warenkorb sind
[*]Wie mache ich es mit der Datenbank? sind load- und save-Methoden in den Klassen geeignet?
[*]Ich würde eine Überklasse erstellen, die z.B. den Zugriff auf die Datenbank (also den Cursor enthält), diese würde ich dann bei jedem neuerstellen eines Objektes als Parameter übergeben. Kann man das als guten Stil bezeichnen?



Ich hoffe ihr versteht alle, was ich meine.
Ich bin leider nur ausgebildeter Systemintegrator, kann zwar programmieren, aber habe über Programmierstile nie so viel gelernt. Und meine Ideen kommen mir etwas umständlich vor.

Vielen Dank,

liebe Grüße
Chris
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Du solltest Dir mal einen ORM angucken, wie etwa SQLAlchemy oder Elixir. Damit ersparst Du Dir viel manuelle Arbeit bezüglich des Ladens und Speicherns. Zudem hast Du ggf. eine Abstraktion über SQL, d.h. Du brauchst kein SQL formulieren.

Du hast zwar die Namen von Klassen gepostet, die implizit eine grundsätzliche Idee des Konzeptes dahinter induzieren, aber die exakten Zusammenhänge und Restriktionen dahinter bleiben eher wage. Insofern kann man dazu kaum etwas sinnvolles schreiben.

Das Zusammenfassen in eine "Menge" kann durchaus von der GUI abhängig sein, genauer vom Toolkit. Bei Qt sind die für spezialisierte Daten gebräuchlichen UI-Elemente gemäß des MVC-Patterns aufgebaut, d.h. es gibt verschiedene Views, die in der Lage sind Modelle anzuzeigen. Deine Datenobjekte müssen also irgend wie in ein Modell überführt werden, welches dann von einem passenden View angezeigt werden kann.

Qt bietet da sogar schon fertige Modelle, die direkt mit einer Datenbank kommunizieren können. Allerdings fällt dann die Zusammenarbeit mit einem der oben genannten ORM flach. Irgend wer im Forum hatte sich afair aber mal damit befasst, auszuloten, inwiefern man mit SQLAlchemy und Qt am besten zusammenarbeitet.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
hendrikS
User
Beiträge: 420
Registriert: Mittwoch 24. Dezember 2008, 22:44
Wohnort: Leipzig

sprudel hat geschrieben:Das ganze ist keine Hausaufgabe, sondern reine Hobbysache.
Also Du musst Dich für nichts rechtfertigen.Ich würde sagen, sofern ein gewisses Eigenengagement erkennbar ist, werden sogar Hausaufgaben aktiv unterstützt.
sprudel hat geschrieben:[*]Produktdefintion
[*]Lager
[*]Kunde
[*]Transaktion (Verkaufsvorgang)
Für Produkt, Lager, Kunden Klassen zu definieren ist sicher nicht verkehrt. Eine transaktion stelle ich mir eher als Methode vor.
sprudel hat geschrieben:[*]Wie fasst man Produkte am intelligentesten in Menge zusammen? Ich möchte ja in der GUI dann z.B. ein Mengenfeld haben, wieviele Produkte also im Warenkorb sind
Wie wäre es mit einem dict? Die Produkte als Schlüssel. Die Werte hinter dem Schlüssel als Anzahl der Produkte.
sprudel hat geschrieben:[*]Wie mache ich es mit der Datenbank? sind load- und save-Methoden in den Klassen geeignet?
Sicherlich nicht verkehrt.
sprudel hat geschrieben:[*]Ich würde eine Überklasse erstellen, die z.B. den Zugriff auf die Datenbank (also den Cursor enthält), diese würde ich dann bei jedem neuerstellen eines Objektes als Parameter übergeben. Kann man das als guten Stil bezeichnen?
Weiss nicht wozu hier eine Überklasse notwendig ist. Möglicherweise verstehe ich hier nicht genau, was Du machen möchtest.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

hendrikS hat geschrieben:
sprudel hat geschrieben:[*]Wie mache ich es mit der Datenbank? sind load- und save-Methoden in den Klassen geeignet?
Sicherlich nicht verkehrt.
Imho schon. Wieso sollte ein Produkt wissen, wie es sich in eine DB speichert? Den "Fehler" habe ich früher auch gerne gemacht, bis ich hier im Forum eines besseren belehrt wurde. Besser wäre eine Funktion / Klasse, die weiß, wie man ein Produkt in eine DB schreibt. Aber wie oben erwähnt würde ich bei Klassen sofort an einen ORm denken.

@spudel: Evtl. wäre auch Camelot etwas für Dich!
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
hendrikS
User
Beiträge: 420
Registriert: Mittwoch 24. Dezember 2008, 22:44
Wohnort: Leipzig

Hyperion hat geschrieben:Imho schon.
Ich habe nurz kur drüber nachgedacht. Habe mir das aber eher banal vorgestellt.Das Lager hat eine Methode save, die aufgerufen wird, wenn entweder ein Produkt aus dem Lager entnommen oder eingefügt wird. Dann macht man noch irgendwie die Datenbank bekannt und den Rest erledigt die Methode.
Mal ganz abstrakt formuliert. Allerdings habe ich hier keine Praxiserfahrung und weiss nicht ab es tatsächlich so banal ist.
BlackJack

@hendrikS: `Transaktion` ist insofern keine Methode, als dass das ein Datensatz ist, der gespeichert werden muss. Wenn man dazu Objekte in einem RDMS speichern möchte, dann macht ein `Transaktion`\s-Objekt IMHO Sinn.

Waren und Mengen in einem Dictionary eher weniger wenn das am Ende eh als ein "Objekt" in die DB geschrieben werden soll. Also zumindest wenn man den ganzen DB-Kram nicht selber schreiben möchte, sondern ein ORM verwendet.

@sprudel: Das mit der Menge würde ich einfach zum Produkt (bzw. einer Referenz darauf) mit in den entsprechenden Datensatz packen. Also so wie das auf Lagerlisten und Bestellformularen auch auf Papier ist, bzw. ja auch in den meisten Onlineshops auch so gemacht wird. Also zum Beispiel einen Typ `Item` der eine Referenz auf ein Produkt und die Anzahl enthält. Das kann man sowohl für das `Lager` als auch für die `Transaktion` verwenden.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

@hendrikS:
Es geht hier ja um Persitenz und da gibt es irgend wie keinen kleinsten gemeinsamen Nenner. Wo zieht man die Grenze? Vielleicht will man ja neben einem DB Zugriff noch eine Serialisierung in XML. Wieso dann nicht auch noch JSON? Klar kann man solche Features a priori wegspezifizieren, aber was passiert, wenn Du später so etwas hinzufügen willst? Die Methoden "save" und "load" sind dann schon "belegt". Also musst Du dann etwas wie "save_xml" in die Klasse einfügen; um die API nicht zu brechen darfst Du dann aber "save" nicht in "save_sql" abändern.

Insofern ist es doch sinnvoller, eine spezialisierte Klasse für die Persistenzverwaltung von bestimmten Objekten zu schreiben. Schau Dir mal das json-Modul an. Dort wird in den Beispiele auch gezeigt, wie man Encoder (Decoder) für eigene Datentypen schreiben kann. Das ist imho sehr ähnlich zu diesem Problem, da die Serialisierung nicht in den Datentypen verlagert, sondern separat abgehandelt wird.
Zuletzt geändert von Hyperion am Montag 24. Januar 2011, 12:40, insgesamt 1-mal geändert.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

Ich werde heute Abend noch ausführlicher antworten, aber eine Gegenfrage: Wo soll ich überhaupt die Lade- und Speichervorgänge hinschreiben?

Und zu Sqlalchemy und Co: Ich möchte sogar gezielt mein Sql selber schreiben. 1. Will ich etwas lernen, das Ergebnis ist zweitrangig und 2. habe ich das Buch über SyqL doch nicht umsonst gelesen :)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

sprudel hat geschrieben: Und zu Sqlalchemy und Co: Ich möchte sogar gezielt mein Sql selber schreiben. 1. Will ich etwas lernen, das Ergebnis ist zweitrangig und 2. habe ich das Buch über SyqL doch nicht umsonst gelesen :)
Du kannst das doch auch, wenn Du Elixir oder SQLAlchemy benutzt. Aber speziell für die lästigen CRUD-Operationen brauchst Du das dann eben nicht mehr. Und mal ehrlich, dabei lernt man imho nix. Interessant sind ja dann eher Suchabfragen und dabei hättest Du auch damit freie Hand.

Aber ok, man kann auch erst einmal alles von Hand schreiben, um dann später zu der Erkenntnis zu gelangen, dass man sich selber einen ORM nachgebaut hat. Erkenntnisgewinne sind ja an sich schon ein Wert :-) (Und da es ja eh nicht für die Praxis eingesetzt werden soll, noch legitimer)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@sprudel: Das mit dem etwas lernen wollen wirft dann die Frage auf *was* Du lernen willst. SQL per Hand zu schreiben, oder wie man üblicherweise so ein Projekt umsetzt? Ich würde nämlich sogar soweit gehen und sagen das hier SQL per Hand zu schreiben, bedeutet zu lernen wie man es *nicht* macht.

Die Eigenheiten und Einschränkungen von SQL zu kennen, ist auch bei ORMs oft wichtig. Nämlich immer dann wenn man etwas optimieren muss, oder man sich auf Fehlersuche begibt. Das Buch hast Du also nicht umsonst gelesen.
Noah
User
Beiträge: 15
Registriert: Montag 17. Januar 2011, 08:20

Als Hinweis zum Aufbau des Modells:

Bei "richtigen" Warenwirtschaften ist die "Transaktion" (also der Verkaufsvorgang) auch ein eigenes Objekt, ja sogar das zentrale Objekt, um dass sich alles Dreht. Daran hängen Positionen, Angebote, Lieferschein, Rechnung...
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Bei "richtigen" Warenwirtschaftssystemen gibt es kein Feld für den Lagerbestand. Es gibt eigentlich nur eine Tabelle mit Lagerbewegungen, um es mal allgemein zu halten. Es werden keine absoluten Lagerstände erfasst sondern der aktuelle Lagerbestand wird aus den Zubuchungen und Abbuchungen errechnet. Es gibt bestenfalls ein Feld, in dem ein Lagerbestand temporär erfasst wird, damit nicht bei jedem Zugriff die gesamte Berechnung durchgeführt werden muss.

Auch bei den Kunden musst du überlegen, ob dir eine statische Tabelle ausreicht. In einer festgelegten Tabelle kann man zum Beispiel nur eine gewisse Anzahl an Adressen und sonstigen Kontaktdaten erfassen. Normalerweise gibt es eine extra Tabelle für Adressen und eine zum Beispiel für Telefon, Fax und Email.

Es hängt davon ab, wie weit du das treiben willst. Um es einfach nur mal auszuprobieren reichen die vier Klassen für den Anfang
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

So, da bin ich nochmal :)

Da wurde ja so einiges geschrieben, konnte es leider während der Arbeit nur mit dem Handy verfolgen.

Zuallererst:
Ich denke ich bestehe auch auf eine Transaktionsklasse, weil ich alles darauf aufbauen kann. So sehe ich es jedenfalls in meinem Kopf schon als Konzept vor mir, und da ich auch eine informationstechnische Ausbildung besitze, nehme ich mir einfach raus das jedenfalls als geeignet (wenn auch nicht unbedingt optimal) zu bezeichnen.

Es haben sich mir folgende neue Fragen aufgetan:
[*] Welches System empfiehlt ihr mir, um dem direkten Mysql aus dem Weg zu gehen? SQLAlchemy? Wenn ja, wie eignet sich das?
[*]Wie würdet ihr es mit dem Importieren machen? Ich werde ja wohl eine Datei anlegen, für jede Klasse. Wo importieren?
[*]Was empfehlt ihr als Basisklasse, also die Klasse die z.B. im Normalfall den Zugriff auf die Datenkbank regeln würde? Brauch man überhaupt noch eine?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

sprudel hat geschrieben: Es haben sich mir folgende neue Fragen aufgetan:
[*] Welches System empfiehlt ihr mir, um dem direkten Mysql aus dem Weg zu gehen? SQLAlchemy? Wenn ja, wie eignet sich das?
Dazu solltest Du Dir mal SQLAlchemy oder auch Elixir angucken. Übrigens würde ich MySQL aus dem Weg gehen und auf Postgres setzen, wenn nicht für den Anfang auch SQLite ausreicht.
sprudel hat geschrieben: [*]Wie würdet ihr es mit dem Importieren machen? Ich werde ja wohl eine Datei anlegen, für jede Klasse. Wo importieren?
Also diese Frage offenbart bei Dir imho noch Schwächen in Deinen Python-Kenntnissen. Zum einen ist diese Aufteilung bei Python unüblich, zum anderen ist das Wissen um grundlegende Funktionalität von Modulen für solch ein Projekt sicherlich von Nöten. Bist du Dir sicher, dass Du für so ein Projekt schon genügend Python und Programmiererfahrung gesammelt hast?

Zudem wenn noch eine GUI oben drauf kommt...
sprudel hat geschrieben: [*]Was empfehlt ihr als Basisklasse, also die Klasse die z.B. im Normalfall den Zugriff auf die Datenkbank regeln würde? Brauch man überhaupt noch eine?
Auch diese Frage ist viel zu allgemein. Eine Basisklasse impliziert, dass man von dieser Ableiten will. Das ist aber ja schon recht speziell und nicht allgemein zu beantworten. Ob man parallel mehrere Verbindungen zu einer DB haben möchte, ist auch eher ein Spezialthema und vom Kontext abhängig.

Imho solltest Du das ganze eine Nummer kleiner angehen und Dich erst einmal mit den Grundlagen intensiver befassen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

Hyperion hat geschrieben: Dazu solltest Du Dir mal SQLAlchemy oder auch Elixir angucken. Übrigens würde ich MySQL aus dem Weg gehen und auf Postgres setzen, wenn nicht für den Anfang auch SQLite ausreicht.

Habe ich mir beides schon mal angeschaut, werde ich mich wohl mal reinarbeiten :)
Also diese Frage offenbart bei Dir imho noch Schwächen in Deinen Python-Kenntnissen. Zum einen ist diese Aufteilung bei Python unüblich, zum anderen ist das Wissen um grundlegende Funktionalität von Modulen für solch ein Projekt sicherlich von Nöten. Bist du Dir sicher, dass Du für so ein Projekt schon genügend Python und Programmiererfahrung gesammelt hast?

Zudem wenn noch eine GUI oben drauf kommt...


Auch wenn es nicht so wirkt, aber ich habe durchaus schon viel mit Python gearbeitet. Ich bin nur teilweise etwas überfordert mit den unterschiedlichen Gepflogenheiten. Jede Sprache ist da eben etwas anders. Sag mir doch am besten einfach wie man es am besten macht :)
Insbesondere bei der GUI-Entwicklung habe ich bereits sehr viel mit wxPython gearbeitet. Die Sache bei mir ist eben die, dass ich als Systemintegrator hauptsächlich mal für kleine Aufgaben eine GUI bastle. Mehr nicht. Aber das kriege ich hin :)


Auch diese Frage ist viel zu allgemein. Eine Basisklasse impliziert, dass man von dieser Ableiten will. Das ist aber ja schon recht speziell und nicht allgemein zu beantworten. Ob man parallel mehrere Verbindungen zu einer DB haben möchte, ist auch eher ein Spezialthema und vom Kontext abhängig.


Eine Basisklasse wäre wohl sinnvoll, weil man die Datenkbankverbindung dort "ablegen" kann. In dieser würde ich dann auch die ganzen Einzelaufgaben ablegen. Oder würdest du es anders machen? Wenn ja, sag mir bitte wie. Wird ja auch bei jeder Sprache irgendwie anders gemacht. Auch würde ich das laden und speichern von Objekten (Produkte, Kunden) in diese auslagern. Oder besser doch nicht?

Imho solltest Du das ganze eine Nummer kleiner angehen und Dich erst einmal mit den Grundlagen intensiver befassen.


Das Problem dabei ist hauptsächlich: Syntaktisch habe ich es bereits drauf, aber die Art wie man in Python programmiert scheint sehr viel anders zu sein, als in anderen Sprachen. Klein "angefangen" habe ich bereits desöfteren. Ich möchte es nun auch mal in Groß probieren. Und so wie ich mir das Kassensystem vorstelle (besserer Taschenrechner mit Rechnungsdruck, vielleicht noch ne kleine Lagerverwaltung, aber kein Waenwirtschaftssystem), wird es sowieso nicht mehr nutzen haben, als vielleicht mal schnell auf dem Flohmarkt.
[/quote]
BlackJack

@sprudel: Ich habe bis jetzt nur etwas mit dem Django-ORM und SQLAlchemy gemacht. Angeschaut hatte ich mir noch SQLObject. Für SQLAlchemy (SA) hatte ich mich entschieden, weil es um eine bereits bestehende Datenbank ging und ich den Eindruck hatte, das SA einem da am wenigsten Beschränkungen auferlegt, weil man beliebige Tabellen(teile) auf nahezu beliebige Klassen abbilden kann, wenn es sein muss. Selbst wenn man SQL hat, was nicht auf Klassen abgebildet wird, kann man das mit SA unabhängig von einem konkreten DBMS machen.

Das ist jetzt für Dein Projekt, wo Du die Datenbank selbst entwerfen kannst, natürlich nicht so wichtig. Und Elixir, was auf SA aufbaut, wurde ja auch schon erwähnt. Das macht das deklarieren von Klassen und Beziehungen etwas einfacher als es mit SA "zu Fuss" zu machen.

Deinen zweiten Punkt verstehe ich nicht ganz!? Und eine Datei pro Klasse ist nicht besonders "pythonisch". Eine Datei repräsentiert ein Modul und ein Modul sollte zusammengehörige "Dinge" enthalten. Wenn Du also eine Klasse hast, die eine Transaktion darstellt, und eine die eine einzelne Position auf so einer Transaktion darstellt, macht es durchaus Sinn, die im gleichen Modul zu haben. Ich persönlich stecke solche "Model-Klassen" in der Regel *alle* in ein Modul bis ich den Eindruck habe, dass es deutlich zu viele werden. Dann mach ich mich daran zu überlegen, wie man das sinnvoll in ein oder zwei Module zerlegen kann.

Der letzte Punkt erledigt sich wahrscheinlich von selbst, wenn Du Dir eine Einführung, ein Tutorial oder ein erklärendes Video zu einem der ORMs angeschaut hast.
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

Mal ne Frage: Würdet ihr eigentlich das Sqlalchemy direkt in die entsprechenden Klassen (Transaction, Product, usw.) einbauen, oder spezielle Klassen schreiben, die dann zu SQLALchemy gehören, und von dort aus dann die normalen Objekte generieren?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Direkt in die Klassen einbauen. Proxy-Objekte führen schlussendlich dazu dass du Duplikation von Daten hast, wenn du dich nicht schlau anstellst und eine völlig nutzlose Zusatzebene.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

Ich weiß, es ist eine blöde Frage, und hier so eigentlich nicht erwünscht.
Aber kann mir hier jemand mal ein grobes Beispiel (gerne auch schemenhaft) einer Beispielklasse (z.B. Transaction) machen, so wie ich das in SQLAlchemy lösen müsste?


Ich stelle mir das im groben (ohne SqlAlchemy) so vor:

Code: Alles auswählen

class Transaction(object):
	products = []
	stornos = []
	notes = []
	payed = False
	

	def addProduct(self,pobj):
		self.products.append(pobj)
	
	def getTotal(self):
		total = 0.00
		for pobj in self.products:
			total += pobj.getPrice()
		return total

Jetzt mal nur als Beispiel.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ich sehe 4 Fehler. Oder 2 .. je nachdem wie man das betrachten will.
Willst du die Datenfelder nicht lieber in ein `__init__` schieben und an das Objekt binden?
Antworten