Schöner Coden - Tipps?

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.
Antworten
T.T. Kreischwurst
User
Beiträge: 52
Registriert: Dienstag 2. Februar 2016, 10:56

Liebes Forum,

ich bin auf der Suche nach Literatur bzw. aktiver Hilfe zum Thema "Entwurfsmuster, OOP, OOA" oder allgemein: wie man seinen Code nicht prozedural vergewaltigt.
Zum Hintergrund: ich programmiere Python hobbymäßig, habe einige Bücher und Tutorials durchgearbeitet und einge kleine eigene Programmprojekte durchgecodet. Gut und schön, habe einiges dabei gelernt und die Scripte funktionieren sogar soweit - mir ist allerdings klar, dass funktionierender Code noch lange nicht guter Code ist. Zwar versuche ich, das ganze so weit wie möglich objektorientiert zu gestalten, aber mein WIssen dazu beschränkt sich auf die paar Anmerkungen, die in der Grundlagenliteratur zum Programmieren gegeben werden. Ich würde das gerne etwas vertiefen und strukturierter lernen, vorzugsweise (aber nicht zwangsläufig exklusiv) anhand von Python.
Habt ihr Tipps zu guten, einsteigerfrundlichen bzw. nicht "Mathematiker only" Büchern zu diesem Thema? Oder wie ich das sonst weiter verbessern kann? Leider ist in meinem Bekannten-/Verwandtenkreis niemand Programmierer...
Danke euch schonmal!
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Das einzige was wirklich hilft ist Erfahrung sammeln. Du kannst natürlich Artikel und Bücher über irgendwelche Entwurfsmuster lesen aber falsch angewendet produzierst du damit auch keinen schönen Code. Um ein Entwurfsmuster richtig anzuwenden, musst du verstanden haben welches Problem es löst und dass kann man eigentlich nur wenn man damit schon einmal konfrontiert war. Stößt du erstmal auf ein Problem, wirst du es allerdings auch so früher oder später merken und von selbst zu einem Entwurfsmuster kommen dass das Problem löst.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@T.T. Kreischwurst: leider gibt es kaum gute Bücher oder sonstige Quellen. Meist werden Entwurfsmuster anhand von Java erklärt, oder Java-Code der 1:1 nach Python übersetzt wurde. Dabei sind die meisten "Rezepte" für Python gar nicht nötig oder würden ganz anders umgesetzt werden. Die Beispiele sind meistens so trivial oder so an den Haaren herbeigezogen, dass man für das eigene Problem so gut wie nichts lernt. Erfahrung kommt mit der Zeit; indem man etwas ausprobiert und merkt, dass es nicht funktioniert, weil es schwer erweiterbar ist und deshalb nochmal komplett umgeschrieben werden muß. Diesen Lernprozess kann Dir kein Buch abnehmen. Und dann gibt es immer noch dieses Forum hier, wo Dir jeder objektorientierte Entwurf auseinandergenommen wird.
nezzcarth
User
Beiträge: 1635
Registriert: Samstag 16. April 2011, 12:47

Sirius3 hat geschrieben:@T.T. Kreischwurst: leider gibt es kaum gute Bücher oder sonstige Quellen. Meist werden Entwurfsmuster anhand von Java erklärt, oder Java-Code der 1:1 nach Python übersetzt wurde. Dabei sind die meisten "Rezepte" für Python gar nicht nötig oder würden ganz anders umgesetzt werden.
Es gibt in "Python 3 Object Oriented Programming" von Dusty Phillips auch was zu Entwurfsmustern; da würde mich interessieren, wie brauchbar das ist (er schreibt immerhin von "Python Design Patterns", was ja schon mal ganz gut klingt...)
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@nezzcarth: ja, die Design-Pattern werden schön beschrieben und bei vielen ist auch der Hinweis dabei, dass Python first-class Funktionen hat und deshalb der Klassenüberbau aus Java unnötig ist und vom eigentlichen Pattern nicht viel übrig bleibt.

Und dann kommt beim "Abstract factory pattern" so ein Hammer:

Code: Alles auswählen

class FranceDateFormatter:
    def format_date(self, y, m, d):
        y, m, d = (str(x) for x in (y,m,d))
        y = '20' + y if len(y) == 2 else y
        m = '0' + m if len(m) == 1 else m
        d = '0' + d if len(d) == 1 else d
        return("{0}/{1}/{2}".format(d,m,y))

class USADateFormatter:
    def format_date(self, y, m, d):
        y, m, d = (str(x) for x in (y,m,d))
        y = '20' + y if len(y) == 2 else y
        m = '0' + m if len(m) == 1 else m
        d = '0' + d if len(d) == 1 else d
        return("{0}-{1}-{2}".format(m,d,y))

class FranceCurrencyFormatter:
    def format_currency(self, base, cents):
        base, cents = (str(x) for x in (base, cents))
        if len(cents) == 0:
            cents = '00'
        elif len(cents) == 1:
            cents = '0' + cents
        digits = []
        for i,c in enumerate(reversed(base)):
            if i and not i % 3:
                digits.append(' ')
            digits.append(c)
        base = ''.join(reversed(digits))
        return "{0}€{1}".format(base, cents)

class USACurrencyFormatter:
    def format_currency(self, base, cents):
        base, cents = (str(x) for x in (base, cents))
        if len(cents) == 0:
            cents = '00'
        elif len(cents) == 1:
            cents = '0' + cents
        digits = []
        for i,c in enumerate(reversed(base)):
            if i and not i % 3:
                digits.append(',')
            digits.append(c)
        base = ''.join(reversed(digits))
        return "${0}.{1}".format(base, cents)

class USAFormatterFactory:
    def create_date_formatter(self):
        return USADateFormatter()
    def create_currency_formatter(self):
        return USACurrencyFormatter()

class FranceFormatterFactory:
    def create_date_formatter(self):
        return FranceDateFormatter()
    def create_currency_formatter(self):
        return FranceCurrencyFormatter()

factory_map = {
    "US": USAFormatterFactory,
    "FR": FranceFormatterFactory
}

country_code = "US"
formatter_factory = factory_map.get(country_code)()
Klassen, die nur aus einer Funktion bestehen. Führende Nullen werden über String-Länge hinzugefügt. return mit unnötigen Klammern. Der Code für US und FR ist fast identisch kopiert. Und die eigentlichen Factory-Klassen bestehen eigentlich auch nur aus Funktionen. Zum Schluß wird get verwendet, obwohl ein Index-Zugriff einen schönen KeyError bei fehlendem country_code liefern würde, statt eines undurchsichtigen TypeErrors.

Würde man das ganze mehr Python-like schreiben

Code: Alles auswählen

def format_date_generic(y, m, d, format):
    if y < 100:
        y += 2000
    return format.format(y=y, m=m, d=d)

def format_date_france(y, m, d):
    return format_date_generic(y, m, d, "{d:02d}/{m:02d}/{y:04d}")

def format_date_usa(y, m, d):
    return format_date_generic(y, m, d, "{m:02d}-{d:02d}-{y:04d}")

def format_currency_generic(base, cents, format, seperator):
    blocks = []
    while base > 1000:
        base, remainder = divmod(base, 1000)
        blocks.append("{0:03d}".format(remainder))
    blocks.append("{0}".format(base))
    base = seperator.join(reversed(blocks))
    return format.format(base, cents)

def format_currency_france(base, cents):
    return format_currency_generic(base, cents, "{0}€{1:02d}", " ")

def format_currency_usa(base, cents):
    return format_currency_generic(base, cents, "${0}.{1:02d}", ",")

formatter_map = {
    "US": {'date': format_date_usa, 'currency': format_currency_usa},
    "FR": {'date': format_date_france, 'currency': format_currency_france},
}

country_code = "US"
formatter = formatter_map[country_code]
print(formatter["currency"](1345, 7))
bleibt von der Factory kaum noch etwas übrig, außer einem Wörterbuch, in dem man die Funktionen direkt nachschlagen kann.
Antworten