Problem
Ich arbeite an einem Python-Projekt, dessen Hauptklasse ein bisschen " God Object " ist. Es gibt so verdammt viele Attribute und Methoden!
Ich möchte die Klasse umgestalten.
Bisher…
Für den ersten Schritt möchte ich etwas relativ Einfaches tun; Aber als ich den einfachsten Ansatz ausprobierte, wurden einige Tests und vorhandene Beispiele gebrochen.
Grundsätzlich hat die Klasse eine lange Liste von Attributen - aber ich kann sie mir klar ansehen und denken: "Diese 5 Attribute sind verwandt ... Diese 8 sind auch verwandt ... und dann gibt es den Rest."
getattr
Ich wollte im Grunde nur die verwandten Attribute in einer diktartigen Hilfsklasse gruppieren. Ich hatte das Gefühl __getattr__
, ideal für den Job zu sein. Also habe ich die Attribute in eine separate Klasse verschoben und natürlich __getattr__
seine Magie perfekt umgesetzt ...
Am ersten .
Aber dann habe ich versucht, eines der Beispiele auszuführen. Die Beispielunterklasse versucht, eines dieser Attribute direkt (auf Klassenebene) festzulegen . Da sich das Attribut jedoch nicht mehr "physisch" in der übergeordneten Klasse befand, wurde die Fehlermeldung angezeigt, dass das Attribut nicht vorhanden war.
@Eigentum
Ich habe dann über den @property
Dekorateur gelesen . Aber dann habe ich auch gelesen, dass es Probleme für Unterklassen schafft, die tun wollen, self.x = blah
wenn x
es sich um eine Eigenschaft der übergeordneten Klasse handelt.
Gewünscht
- Lassen Sie den gesamten Client-Code weiterhin verwenden
self.whatever
, auch wenn sich diewhatever
Eigenschaft des übergeordneten Elements nicht "physisch" in der Klasse (oder Instanz) selbst befindet. - Gruppieren Sie verwandte Attribute in diktartigen Containern.
- Reduzieren Sie das extreme Rauschen des Codes in der Hauptklasse.
Zum Beispiel möchte ich das nicht einfach ändern:
larry = 2
curly = 'abcd'
moe = self.doh()
Das mögen:
larry = something_else('larry')
curly = something_else('curly')
moe = yet_another_thing.moe()
… Weil das immer noch laut ist. Obwohl dies ein einfaches Attribut erfolgreich zu etwas macht, das die Daten verwalten kann, hatte das Original 3 Variablen und die optimierte Version hat immer noch 3 Variablen.
Mit so etwas würde ich jedoch gut zurechtkommen:
stooges = Stooges()
Und wenn eine Suche nach self.larry
fehlschlägt, prüft etwas, stooges
ob larry
es vorhanden ist. (Es muss aber auch funktionieren, wenn eine Unterklasse dies larry = 'blah'
auf Klassenebene versucht .)
Zusammenfassung
- Möchten Sie verwandte Gruppen von Attributen in einer übergeordneten Klasse durch ein einzelnes Attribut ersetzen, in dem alle Daten an anderer Stelle gespeichert sind
- Möchten Sie mit vorhandenem Client-Code arbeiten, der (z. B.)
larry = 'blah'
auf Klassenebene verwendet - Sie möchten weiterhin zulassen, dass Unterklassen diese überarbeiteten Attribute erweitern, überschreiben und ändern, ohne zu wissen, dass sich etwas geändert hat
Ist das möglich? Oder belle ich den falschen Baum an?
quelle
Antworten:
Nachdem ich ein Python "God Object" geschrieben und dann überarbeitet habe, sympathisiere ich. Ich habe das ursprüngliche Objekt anhand von Methoden in Unterabschnitte unterteilt. Zum Beispiel sah das Original wie folgt aus:
Die Stuff-Methode ist eine in sich geschlossene "Arbeitseinheit". Ich habe es in eine neue Klasse migriert, die das Original instanziiert. Dies zog auch die notwendigen Eigenschaften heraus. Einige wurden nur von der Unterklasse benutzt und konnten sich geradeaus bewegen. Andere wurden geteilt und in eine gemeinsame Klasse versetzt.
Das "God-Objekt" erstellt beim Start eine neue Kopie der gemeinsam genutzten Klasse, und jede der neuen Unterklassen akzeptiert einen Zeiger als Teil ihrer init-Methode. Hier ist zum Beispiel eine abgespeckte Version des Mailers:
Es wird einmal erstellt und von den verschiedenen Klassen gemeinsam genutzt, die Mailing-Funktionen benötigen.
Erstellen Sie für Sie eine Klasse
larry
mit den Eigenschaften und Methoden, die Sie benötigen. Überall, wo der Kunde sagt,larry = blah
ersetzen Sie es durchlarryObj.larry = blah
. Dadurch werden Dinge in Unterprojekte migriert, ohne die aktuelle Schnittstelle zu beschädigen.Das einzige andere, was zu tun ist, ist nach "Arbeitseinheiten" zu suchen. Wenn Sie einen Teil des "Gott-Objekts" in eine eigene Methode verwandeln möchten, tun Sie dies . Aber stellen Sie die Methode außerhalb . Dadurch müssen Sie eine Schnittstelle zwischen den Komponenten erstellen.
Wenn Sie diese Grundlagen schaffen, kann alles andere ihr folgen. Ein Teil des Hilfsobjekts zeigt beispielsweise, wie es mit dem Mailer verbunden ist:
Konzentrieren Sie sich auf die kleinstmögliche Arbeitseinheit und bewegen Sie sie heraus. Dies ist einfacher und ermöglicht es Ihnen, schnell mit dem Setup zu spielen. Sehen Sie sich keine Eigenschaften zum Verschieben von Gegenständen an, sie sind in den meisten Fällen eine Ergänzung zu den Aufgaben , die mit ihnen ausgeführt werden. Was übrig bleibt, nachdem Sie sich mit den Methoden befasst haben, sollte wahrscheinlich im ursprünglichen Objekt bleiben , da es Teil des gemeinsam genutzten Status ist.
Aber sollten die neuen Objekte akzeptieren nun die Eigenschaften , die sie als init Variablen benötigen, den Anrufer nicht berühren Eigenschaft Objekte. Sie geben dann alle erforderlichen Werte zurück, mit denen der Aufrufer die freigegebenen Eigenschaften nach Bedarf aktualisieren kann. Dies hilft, die Objekte zu entkoppeln und sorgt für ein robusteres System.
quelle