2015-04-06 19 views
6

Ich erinnere mich, dass ich vor ein paar Jahren statische Initialisierer verwendet habe, um Setup-Operationen auf Klassenebene aufzurufen. Ich erinnere mich daran, dass ich sehr bizarre Verhaltensweisen hatte und ich beschloss einfach, mich von ihnen fern zu halten. Vielleicht lag es daran, dass ich die Bestellung von oben nach unten durcheinander brachte oder ein Neuling war. Aber ich stoße auf die Notwendigkeit, sie noch einmal zu besuchen, und ich möchte sicherstellen, dass es keinen besseren Weg gibt, der ebenso prägnant ist.Legitime Verwendung für statische Initialisierung?

Ich weiß, dass es nicht modisch ist, aber ich habe oft datengesteuerte Klassen, die eine statische Liste von Instanzen verwalten, die aus einer Datenbank importiert werden.

public class StratBand { 
     private static volatile ImmutableList<StratBand> stratBands = importFromDb(); 

     private final int minRange; 
     private final int maxRange; 

     private static ImmutableList<StratBand> importFromDb() { 
      //construct list from database here 
     } 
     //constructors, methods, etc 
} 

Wenn ich Dutzende von tabellengesteuerten Klassen wie diese haben, ist dieses Muster sehr prägnant (ja, ich weiß, dass es eng koppelt die Klasse mit einer Datenquelle/Instanzen).

Als ich jedoch die Güte von Google Guava entdeckte, möchte ich den EventBus verwenden, um die statische Liste zu aktualisieren, wenn ein bestimmtes Ereignis gepostet wurde. Ich würde eine statische finale boolesche Variable erstellen, nur um eine statische Methode aufzurufen, die die Registrierung initialisiert hat.

Dies wurde sehr schnell nervig, weil der Compiler würde Warnungen über die abonnierte Variable nie verwendet werden. Außerdem fügte es einfach Unordnung hinzu. Ich frage mich, ob es koscher ist, den statischen Initialisierer zu verwenden, und es gibt wirklich keinen besseren Weg, wenn ich das nicht in zwei oder mehr Klassen entkopple. Gedanken?

public class StratBand { 
      private static volatile ImmutableList<StratBand> stratBands = importFromDb(); 

      static { 
      MyEventBus.get().register(new Object() { 
        @Subscribe 
        public void refresh(ParameterRefreshEvent e) { 
         stratBands = importFromDb(); 
        } 
       }); 
      } 

      private final int minRange; 
      private final int maxRange; 

      private static ImmutableList<StratBand> importFromDb() { 
       //construct list from database here 
      } 
      //constructors, methods, etc 


    } 
+2

Es ist "nicht modisch", weil es, wie Sie herausgefunden haben, zu "sehr bizarren Verhaltensweisen" führt. Es ist im Allgemeinen eine bessere Praktik, diese Dinge überhaupt nicht statisch zu haben, um sie dort zu übergeben, wo sie explizit benötigt werden (oder implizit durch die Abhängigkeitsinjektion). Statische Initialisierer sind in Ordnung, wenn sie nur Singletons erstellen oder reine Berechnungen durchführen, aber sie sollten im Allgemeinen nicht mit dem Dateisystem, Datenbanken oder irgendetwas anderem interagieren. –

+0

Ich habe darauf gewartet, dass jemand das sagt. Und ich bekomme das DI-Paradigma, das ich schätze. Aber bis wir bereit sind, auf ein DI-gestütztes Framework hochzuskalieren, hoffe ich, dass wir etwas länger halten können. – tmn

+1

Sie müssen DI hier nicht verwenden, aber ich wäre überrascht, wenn Sie dies überhaupt zur Arbeit bringen könnten, und es wird äußerst fragil bleiben, schwer zu testen sein, und ehrlich gesagt würde es mehr Aufwand erfordern, als es richtig zu machen würde nehmen. –

Antwort

3

Also ich frage mich, ob es koscher ist der statische Initialisierer

Das Komische zu verwenden, ist, dass

private static final boolean subscribed = subscribe(); 

und

private static final boolean subscribed; 
static { 
    subscribed = subscribe(); 
} 

get kompiliert zu genau der gleicher Bytecode. Die Verwendung der unnötigen statischen Variablen ist also streng schlechter.


Aber bis wir bereit sind, zu einem DI-driven Rahmen zu skalieren,

Entdecken Guice. Nenne es nicht Framework (obwohl es ist). Es ist einfach zu bedienen und lassen Sie uns static loswerden.

Oder tun Sie es manuell. Schreiben Sie Ihre Klasse neu, indem Sie alle statischen Modifikatoren löschen und an allen Stellen übergeben, an denen Sie sie benötigen. Manchmal ist es ziemlich ausführlich, aber die Angabe von Abhängigkeiten erlaubt es Ihnen, Klassen isoliert zu testen.

So wie es ist, können Sie StratBand nicht testen, ohne die Datenbank zu treffen, egal wie trivial die zu testende Methode ist. Das Problem ist die Kopplung von jeder StratBand Instanz an die Liste aller StratBand s.

Darüber hinaus können Sie das Verhalten nicht abhängig von den stratBands Inhalt testen, wie es immer aus der DB geladen wird (sicher, Sie können Ihre DB entsprechend füllen, aber es ist ein großer Schmerz).

Für den Anfang würde ich StratBandManager (oder StratBands oder was auch immer Name, die Sie mögen) erstellen und verschieben Sie alle statische Funktionalität auf sie. Um einfach den Übergang, würde ich eine temporäre Klasse mit statischen Helfer wie

private static StratBandManager stratBandManager = new StratBandManager(); 
public static ImmutableList<StratBand> stratBands() { 
    return stratBandManager.stratBands(); 
} 

es schaffen Dann deprecate alle und ersetzen Sie es durch DI (mit Guice oder es manuell tun).


I find Guice nützlich auch für kleine Projekte. Der Overhead ist winzig, da es oft keine oder kaum Konfiguration gibt.

+0

Yeah Ich habe viele Stunden damit verbracht Guice zu studieren, obwohl ich es noch nicht angewendet habe und gerne machen würde. Angesichts unseres aktuellen Geschäftsumfelds und unserer Funktion haben wir den minimalistischen Ansatz gewählt ... und mit minimalistisch meine ich, die Anzahl der Klassen zu konsolidieren und zu reduzieren, wenn es die Dinge vereinfacht. Ich werde auf jeden Fall Ihren Übergangsvorschlag berücksichtigen müssen, da die Gemeinschaft ziemlich unnachgiebig ist, wenn es darum geht, DI zu entkoppeln und zu verwenden. – tmn

+0

@ThomasN. Ich würde die Anzahl der Klassen nicht minimieren. Möglicherweise möchten Sie geschachtelte Klassen für die "Manager" verwenden, um die Anzahl der Dateien niedrig zu halten, aber zusätzliche Klassen sind im Wesentlichen kostenlos (mit Ausnahme der Speicherkosten, die für Android von Bedeutung sein können). – maaartinus

+0

Ja, ich mochte verschachtelte Klassen, weil es alles an einem Ort enthält. Ich bin mir im Klaren darüber, dass es bei einem bestimmten Komplexitätsschwellenwert zu Belastbarkeit führen kann. Ich denke, dass ich meinen Ansatz ein wenig weiterentwickeln und Paket-Private-Paradigmen stärker nutzen werde, ebenso wie DI-freundliche Designs. – tmn