2012-11-01 8 views
6

Ich habe eine Java-Enumeration geschrieben, in der die Werte verschiedene Attribute haben. Diese Attribute können in einer der folgenden Arten gespeichert werden:Java enums - Auswahl zwischen Feldern, abstrakten Methoden und Klassenebenen-Map

Verwenden von Feldern:

enum Eenum { 
    V1(p1), 
    V2(p2); 

    private final A attr; 

    public A attr() { return attr; } 

    private Eenum(A attr) { 
    this.attr = attr; 
    } 
} 

Mit abstrakten Methoden:

enum Eenum { 
    V1 { 
    public A attr() { return p1; } 
    }, 

    V2 { 
    public A attr() { return p2; } 
    } 

    public abstract A attr(); 
} 

Mit Klassenstufe Karte:

enum Eenum { 
    V1, 
    V2; 

    public A attr() { return attrs.get(this); } 

    private static final Map<A> attrs; 

    static { 
    ImmutableMap.Builder<Eenum, A> builder = ImmutableMap.builder(); 
    builder.put(V1, p1); 
    builder.put(V2, p2); 
    attrs = builder.build(); 
    } 
} 

Wie sollte ich entscheiden, wann welche bevorzugen?

Danke!

+0

Ich denke, Sie sollten zuerst erklären, wie sie sich beziehen ('Eenum' und' A'). –

+0

@BheshGurung, das sind nur einige zufällig ausgewählte Namen. 'A' ist ein zufälliger Typ. Wenn sie in irgendeiner Weise verwandt wären, würde ich dies ausdrücklich angeben. – missingfaktor

+0

Ich frage mich, wer zum Schließen gewählt und warum. – missingfaktor

Antwort

0

(ich meine eigene Frage bin zu beantworten, so dass ich einige Dinge teilen kann ich gelernt, während Dinge auszuprobieren.)

Hier sind die Fragen, die Sie stellen sollten, um zu einer Entscheidung für Ihren speziellen Fall zu kommen:

1: Sind die Attributwerte Vorwärtsreferenzen?

Manchmal kann das Attribut V1 einen Verweis auf V2 und umgekehrt benötigen. Dies ist kein seltener Fall.Wenn Sie mit solch einem enum umgehen, würde Ansatz 1 einfach nicht funktionieren. Der Compiler wird sich (zu Recht) über illegale Vorwärtsreferenzen beschweren. Jeder der anderen zwei Ansätze kann verwendet werden.

Jetzt, wenn der Attributwert teuer zu berechnen ist und eine Konstante, möchten Sie, dass es nur einmal berechnet wird. Bei Ansatz 2 müssten Sie lokale Variablen pro Aufzählungswert einführen und die Ergebnisse dort zwischenspeichern. Dies ist ausführlich, bietet Ihnen jedoch eine bessere Leistung. Bei Ansatz 3 werden die Ergebnisse ohnehin nur einmal berechnet und müssen daher keine zusätzliche Arbeit leisten. Dies ist lesbarer, aber etwas weniger performant als Ansatz 2. Design zwischen diesen gemäß den spezifischen Kompromissen, die in Ihrem Fall gerechtfertigt sind.

2: Muss ich Ergebnisse zwischenspeichern?

Siehe den zweiten Absatz der vorherigen Aufzählung.

Wenn keine Vorwärtsreferenzen vorhanden sind, können Sie auch Ansatz 1 verwenden. Aber wenn die Berechnung bei der Berechnung von Attributen komplex ist, sind Sie besser mit einem der beiden anderen Ansätze.

3: Sind die Attribute für alle Enum-Werte relevant?

Wenn nicht, dann sollten Sie logischerweise einen Map hier verwenden. Das heißt, Ansatz 3.

4: Gibt es für einige Enum-Werte irgendwelche Standardwerte für einige Attribute?

Wenn ja, können Sie alle drei Ansätze verwenden, und alle bieten unterschiedliche Kompromisse an.

Mit Ansatz 1: Sie würden einen Hilfskonstruktor definieren, der das Attribut auf den Standardwert initialisiert. Wenn mehrere solcher Attribute vorhanden sind, ist dies möglicherweise kein praktikabler Ansatz.

Mit Ansatz 2: Dies wird tatsächlich wie "vierte" Ansatz Peter Lawrey oben vorgeschlagen. Sie haben eine Methode, die den Standardwert in enum Hauptkörper zurückgibt. Und einige Aufzählungswerte überschreiben diese Methode, um einen anderen Wert zurückzugeben. Dies ist wiederum ziemlich ausführlich.

Mit Ansatz 3: Nur weniger effizient. Gut in jeder anderen Art und Weise.

3

Ich würde das tun, was Sie für das einfachste halten.

Im Allgemeinen schreibe ich keinen Code, der mit Daten implementiert werden kann. Ich würde den ersten verwenden.

Mein eigentlicher Anwendungsfall hat einige Attribute, die nicht relevant sind für alle Enum

Werte können Sie eine Kombination dieser Ansätze verwenden, wenn es sinnvoll, auf einer pro Attribut Basis macht.

Eine vierte Option ist, keine abstrakte Methode zu haben.

enum Eenum { 
    V1 { 
    public A attr() { return p1; } 
    }, 

    V2 { 
    public A attr() { return p2; } 
    }, 
    V3, V4, V5, V6; 

    public A attr() { return defaultA; } 
} 
+0

Einverstanden. Für mich ist die erste Lösung die einfachste und sauberste – darrengorman

+0

Mein tatsächlicher Anwendungsfall hat einige Attribute, die nicht für alle Enum-Werte relevant sind. Wäre eine Karte in diesem Fall besser geeignet? – missingfaktor

+0

Mein tatsächlicher Anwendungsfall hat einige Einschränkungen, die alle drei Ansätze mehr oder weniger gleich lang machen. – missingfaktor

0

Keiner von denen. Tun Sie dies:

interface HasAttr<T> { 
    T attr(); 
} 

enum Eenum implements HasAttr<A> { 

    // use "fields" version - ideally with constructor version 

    public A attr() { 
     return field; 
    } 

} 

Dieses Muster folgt der grundlegenden Abstract Type Entwurfsmuster, die wie für Methode erlaubt:

public void someMethod(HasAttr<A> hasAttr); // pass anything that is HasAttr<a> 

bevorzugt gegenüber dem festen Typ:

public void someMethod(Eenum eenum); // locked into passing an Eenum 

Auch und vor allem Es ist einfacher, sich zu testen, vor allem, wenn Ihr enum reale Verbindungen usw. verwendet.

Ich gewähre Ihnen, all dies gilt nur, wenn das Enum "nichttrivial" ist. Wenn es nur eine einfache alte Enum ist, ich bin einverstanden, es ist nur Code aufblasen (was ich auch verabscheuen)

+0

Das Ausschneiden einer Schnittstelle für jedes Feld (ich habe mehrere Felder in meinem tatsächlichen Code) scheint sinnlos. – missingfaktor

+1

Vor allem, da es keinen Wert hinzufügen wird, für den Fall, mit dem ich es zu tun habe. – missingfaktor

+0

@Bohemian wollen eine Erklärung hinzufügen, warum das besser ist? – darrengorman

Verwandte Themen