Ist ein großer statischer Initialisierer ein Codegeruch?

8

Ich erweitere SimpleExpandableListAdapterin Android. Ich denke nicht, dass der Adapter von Android sehr gut implementiert ist, da seine Konstruktoren eine große Anzahl ziemlich komplizierter Argumente haben und es keine Setter oder Builder gibt. In meiner Klasse sind die meisten dieser Argumente nicht von der aufrufenden Klasse abhängig, daher möchte ich sie intern erstellen. Die Argumente sind jedoch verschachtelte Lists und Arrays von Ints und Strings, die programmgesteuert erstellt werden müssen.

Da vor dem superKonstruktor nichts aufgerufen werden kann und Instanzmethoden nicht aufgerufen werden können, bevor der superAufruf zurückgegeben wird, habe ich derzeit mehrere statische Methoden, die ich vom Aufruf aus superaufrufe:

super(getContext(), initGroupData(), groupLayout, initGroupFrom(), initGroupTo(),
        initChildData(), childLayout, initChildFrom(), initChildTo());

Ich sehe drei Möglichkeiten, dies zu handhaben: Aufrufen statischer Methoden wie jetzt, mit einem großen statischen Initialisierer, der wahrscheinlich dieselben Methoden aufruft, um statische Variablen zu initialisieren, die dann im superAufruf verwendet werden, oder Einkapseln all dieser Methoden in einen Builder.

Ich denke, im Moment neige ich mich zum Baumeister, aber ich frage mich, ob es einen besseren Weg gibt, damit umzugehen.

TBridges42
quelle
1
Was ist mit einer Factory-Methode / -Funktion?
Paŭlo Ebermann

Antworten:

9

Statische Hilfsfunktionen zum Erstellen des Konstruktorarguments sind eine völlig vernünftige Lösung. Diese Funktionen sind jedoch in ihren Operationen begrenzt, da sie jeweils genau ein Argument erzeugen müssen und nicht miteinander kommunizieren können.

Im allgemeinsten Fall, in dem Sie eine Schnittstelle Constructor(A, B, C)an eine benutzerfreundlichere Schnittstelle anpassen möchten Constructor(X, Y), können Sie einen privaten Hilfskonstruktor definieren, der eine private ArgumentObjectund eine Kette zum vorhandenen Konstruktor führt. Der benutzerfreundlichere Konstruktor verkettet sich dann über eine statische Hilfsfunktion mit dem Hilfskonstruktor, um das Argumentobjekt zu erstellen:

class Constructor {
  // constructor you want to wrap
  public Constructor(A a, B b, C c) { ... }
  // better constructor you are defining
  public Constructor(X x, Y y) { this(createArgumentObject(x, y)); }
  // helper constructor using an ArgumentObject
  private Constructor(ArgumentObject ao) { this(ao.a, ao.b, ao.c); }
  // helper to create the argument object
  private static ArgumentObject createArgumentObject(X x, Y y) { ... }
  private static class ArgumentObject { ... }
}

In Sprachen, in denen keine Konstruktorkette innerhalb derselben Klasse verkettet ist (z. B. C ++ 03), müssten Sie eine Zwischenunterklasse für den Hilfskonstruktor definieren.

Diese Technik ist jedoch nur eine Verallgemeinerung Ihrer Verwendung statischer Funktionen in den Konstruktorargumenten. Die anderen von Ihnen vorgeschlagenen Lösungen weisen verschiedene Nachteile auf, weshalb ich sie vermeiden würde, es sei denn, es gibt einen sehr guten Grund, sie zu bevorzugen:

  • Die Implementierung guter Builder erfordert viel Aufwand bei sehr geringem Wert. Wenn der neue Konstruktor einfach genug ist, können Sie auf einen Builder verzichten. Angenommen, die Klasse, die Sie einschließen, führt eine solide Validierung durch, und Sie könnten das Argumentobjekt öffentlich machen, damit Argumente mit der Constructor(new Constructor.Arguments {{ foo = 42; bar = baz; }})Redewendung übergeben werden können.

  • Die Verwendung statischer Variablen, die in einem statischen Initialisierungsblock berechnet werden, ist für wirklich statische Daten sinnvoll. Es muss jedoch darauf geachtet werden, einen globalen Status zu vermeiden. Sofern die Initialisierung nicht sehr einfach ist, sollten Sie statische Funktionen verwenden, um diese Variablen zu initialisieren, damit die Initialisierung testbar ist. Der einzige Vorteil gegenüber der direkten Verwendung statischer Methoden besteht darin, dass die Werte nur einmal berechnet und für alle Initialisierungen wiederverwendet werden.

    Da Ihre Frage angibt, dass diese Initialisierungen komplexer sein könnten, ist die Verwendung statischer Initialisierungsblöcke ein großes Nein-Nein, wenn die Testbarkeit für Sie wichtig ist. (Wenn nicht, haben Sie dringendere Probleme.)

amon
quelle