Sven Rautenberg: Parameter zum Klassenaufruf zwischenspeichern

Beitrag lesen

Moin!

Erstens hat sie eine mangelhafte Signatur: Optionale Elemente gehören ans Ende der Parameterliste, und offenbar ist das Objekt optional.

Nein, ist es nicht. Das Objekt (bzw. dessen Klassenname und die Parameter sind Pflicht).

Wenn es Pflicht ist, kann es nicht überflüssig sein.

Wenn du intern in der Funktion zwar zu dieser Erkenntnis gelangst, hat das dennoch nach außen hin keinerlei Relevanz: Die innere Funktionsweise hat die Außenwelt nicht zu interessieren.

Zweitens: Wieso verlangt die Funktion ein Objekt, dass sie hinterher nicht braucht? Wenn man das offenbar anhand des übergebenen Parameters ermitteln kann, warum wird das nicht außerhalb der Funktion ermittelt?

Weil die Funktion ein Cache für benötigte Objekte anlegt. Ob die Objekte später aber auch tatsächlich benötigt werden, regelt eine externe Controller-Klasse.

Caching ist eine ganz schreckliche Aufgabe für "schöne Architektur", weil sie orthogonal zu allen Strukturen verläuft und mit denselben Grundbausteinen auf nahezu jeder Ebene wirken kann. Die Kunst ist, sich dadurch nicht die Architektur zu zerbröseln.

Ein generell guter Tipp ist, Caching explizit zu separieren und als Decorator-Pattern zu implementieren. Das erfordert ggf. einen geringen Mehraufwand, um für jede normale Klasse auch noch ein vom Interface her identisches Duplikat mit nur der Cache-Funktion zu erstellen (die eigentliche Funktion bleibt in der Originalklasse und wird bei Cache-Miss vom Decorator aufgerufen, das Ergebnis dann im Cache gespeichert und zurückgegeben), aber es hat den unschlagbaren Vorteil, dass man sich gedanklich bei der Nutzung der Klasse eben gerade KEINE Gedanken machen muss, ob man sich jetzt mit dem Cache herumschlägt, oder mit dem Original: Man ruft immer dieselbe Methode des übergebenen Objekts auf.

Wenn man ein Cache-Objekt hat, und der Cache hat einen Eintrag zu den Parametern, kriegt man das Ergebnis hoffentlich schneller, als wenn man die Original-Methode bemühen muss.

Wenn die Funktion die Objekte herstellt, erfüllt sie eine weitere Aufgabe zusätzlich, die ihr nicht zusteht.

Ja, das überlasse ich dann wohl der Controller-Klasse. Ändert aber erstmal nichts an meinem "Problem".

Dein "Problem" ist nach meiner bisherigen Information eher weniger, dass du da eventuell sinnlose Objekte instanziierst, sondern dass es architektonisch Verbesserungsbedarf gibt.

Und es hilft bei der Problembehebung auch nicht, wenn du versuchst, die Darstellung im Sinne deiner gewünschten Lösung zu abstrahieren - das führt gern mal zum XY-Problem: Du hast Problem X und denkst, dass Lösung Y gilt, fragst aber bei Problemen statt zu Problem X (was korrekt wäre) nur zum Problem mit Lösung Y.

Immerhin wissen wir jetzt, dass es irgendwas mit Caching zu tun hat. Verrätst du mehr Details?

Und alle haben sie unterschiedlich lange Parameterlisten des Konstruktors (was auch wieder sehr fragwürdig ist im Hinblick auf Vererbung und Selbstähnlichkeit, aber sofern die Objekte zumindest alle dasselbe Interface gegenüber der Funktion implementieren, kann das mit den notwendigen Unterschieden der Unterklassen durchaus begründet sein).

Sie "extenden" alle die selbe Klasse.

Erklärst du, warum es verschieden lange Konstruktorparameter-Listen gibt?

Deine Beschreibung deutet an, dass die Klassenstruktur sowieso nicht optimal ist.

Controller A sammelt Daten über Model B (welche Daten) und Daten über Model B (welche Bedingungen). Danach vergleicht A B mit C und liefert die Schnittmenge. Die Frage ist halt nur, warum ich die Ergebnisse von, in dem Fall, B instanzieren sollte bevor sie in der Schnittmenge landen? Was ist an der Klassenstruktur nicht optimal?

Das kann ich dir nicht sagen, weil ich keine Details kenne. Die Nennung deines abstrahierten Codes kann ich für die Antwort nicht ernstlich heranziehen, denn da fehlt es am Kontext.

Du hast außerdem außer deinem Gefühl für Unschönheit noch kein Argument GEGEN die Instanziierung gebracht. Das einzige Argument, das mir einfiele, wäre Performance - und das dann gültige Gegenargument ist: Tu nix im Konstruktor, außer die Parameter im Objekt lokal abzuspeichern.

Ja deshalb hätte ich auch eigentlich eher gern die Parameter im Konstruktor. Allerdings kann ich "zur Not" natürlich auch in der externen Logik dafür sorgen, dass die Instanzierung einer "Konvention" erfolgt. Z.B. Objekt instanzieren, dann eine Funktion der Elternklasse aufrufen um die Parameter zu initialisieren.

Ich verstehe nicht ganz genau, was du meinst, aber ich ahne, dass es mit Decorator-Pattern hier einen Ausweg geben könnte.

Das einzige dann noch verbleibende Gegenargument wäre Speicherverbrauch. Um den kommst du aber bei keiner Lösung drum herum, denn ein Array mit den Konstruktorparametern kostet auch Speicher, ebenso wie das Callable.

Schon 2 "einzige"? ;)
Aber ja, am Ende braucht ein Objekt natürlich auch mehr Speicher als ein paar Strings (Klassenname+Parameter) in einem Array.

Bist du dir da sicher?

Ein Array benötigt pro Element 144 Byte. Speicherst du Strings, kommen diese Bytes noch obendrauf, plus ein wenig Overhead für Verwaltungsinformationen.

Für ein Objekt weiß ich es leider nicht genau, aber da die sehr speicheraufwendige Hash-Doppelverlinkte-Liste-Konstruktion des Arrays entfällt, könnte das Rennen am Ende tatsächlich positiv für das Objekt ausgehen. Pro Instanz braucht man ja nur die Instanzvariablen speichern, der Code wird nur einmal benötigt und dürfte in der Regel bereits geladen sein.

- Sven Rautenberg