2015-09-22 10 views
9

Mam dwa wyliczenia, które są powiązane.Dwa powiązane mapowanie podsumowań?

Enum1:

public enum HttpMethodName 
{ 
    GET, POST, PUT, DELETE; 
} 

Enum2:

public enum ProtocolOperation { 
    CREATE(1), RETRIEVE(2), UPDATE(3), DELETE(4), NOTIFY(5); 


    private BigInteger operationId; 

    public BigInteger getOperationId() { 
     return operationId; 
    } 

    private ProtocolOperation(int operationId) { 
     this.operationId = BigInteger.valueOf(operationId); 
    } 
} 

Wartości wyliczenia mają odwzorowanie jako:

Create--> POST 
Retrieve--> GET 
Update--> PUT 
Delete--> DELETE 
Notify---> POST 

Więc jest mapowanie jeden do jednego między wartościami z wyjątkiem tylko dla POST przypadku które mogą mieć dwie wartości: Create lub Notify na podstawie warunku. tam

public enum HttpMethodName 
    { 

     POST(new List(ProtocolOperation.CREATE, ProtocolOperation.NOTIFY)) ,GET (new List(ProtocolOperation.RETRIEVE)) , 
PUT (new List(ProtocolOperation.UPDATE) ,DELETE(new List(ProtocolOperation.DELETE) ; 

     List<ProtocolOperation > ops; 
     HttpMethodName (List<ProtocolOperation> ops) 
     { 
      this.ops = ops; 
     } 
    } 

jest lepszym podejściem ??:

Myślałam o utrzymanie mapowanie jako List

EDIT:

będę chciał mapowanie oba sposoby z HttpMethodName <----> ProtocolOperation

+0

Czy to ostatnia edycja, aby słowo "zarówno" pogrubienie, reakcję na moją odpowiedź? Ponieważ moja odpowiedź ma odwzorowanie w obu kierunkach. – Andreas

+0

użyć można użyć BiMap w Guava – yelliver

+0

Czy spodziewasz się mieć dwie instancje 'Map' lub czy istniejące metody zalecania odpowiedzi są wystarczająco dobre? Być może możesz również podzielić się tym, w jaki sposób ta relacja będzie dalej używana w twojej bazie kodów? –

Odpowiedz

8

Dlaczego Twoje obecne podejście jest niezadowalające?

Bez znajomości swoich obaw mogę tylko zasugerować, aby usunąć boilerplate:

import static ProtocolOperation.*; 

public enum HttpMethodName { 
    GET(RETRIEVE), 
    POST(CREATE, NOTIFY), 
    PUT(UPDATE), 
    DELETE(ProtocolOperation.DELETE); 

    final List<ProtocolOperation> ops; 

    HttpMethodName(ProtocolOperation... ops) { 
     this.ops = Collections.unmodifiableList(Arrays.asList(ops)); 
    } 
} 

UPD:

Jeśli chcesz mieć odwzorowanie w obie strony, ma sens hardcode mapowanie z ProtocolOperation do HttpMethodName pierwszy (jako że nie wymaga listę) i utworzyć prostą metodę wyszukiwania w MttpMethodName:

public enum ProtocolOperation { 
    CREATE(1, HttpMethodName.POST), 
    RETRIEVE(2, HttpMethodName.GET), 
    UPDATE(3, HttpMethodName.PUT), 
    DELETE(4, HttpMethodName.DELETE), 
    NOTIFY(5, HttpMethodName.POST); 

    private BigInteger operationId; 
    private HttpMethodName methodName; 

    public BigInteger getOperationId() { 
     return operationId; 
    } 

    public HttpMethodName getMethodName() { 
     return methodName; 
    } 

    private ProtocolOperation(int operationId, HttpMethodName httpMethodName) { 
     this.methodName = httpMethodName; 
     this.operationId = BigInteger.valueOf(operationId); 
    } 
} 

public enum HttpMethodName { 
    GET, 
    POST, 
    PUT, 
    DELETE; 

    List<ProtocolOperation> getProtocolOperations() { 
     List<ProtocolOperation> ops = new ArrayList<ProtocolOperation>(2); 
     for (ProtocolOperation op : ProtocolOperation.values()) { 
      if (op.getMethodName() == this) { 
       ops.add(op); 
      } 
     } 
     return ops; 
    } 
} 

jak ty mają stałą i małą liczbę wartości, nie trzeba tworzyć ostatecznej mapy statycznej w HttpMethodName, aby zapewnić odwzorowanie odwrotne, wyszukiwanie liniowe jest wystarczająco szybkie dla przypadku.

+0

Ponieważ OP chce mapy od 'ProtocolOperation' do' HttpMethodName', powinieneś był zrobić to w inny sposób, co również wyeliminowałoby potrzebę listy. – Andreas

+0

@Andreas: To by prowadziło do powtarzania wartości wyliczeniowych za każdym razem, aby znaleźć mapowanie ?? –

+0

@Andreas, co sprawia, że ​​myślisz, że OP chce mapować z 'ProtocolOperation' na' HttpMethodName'? Sądząc po jego słowach: "Wartości wyliczeniowe mają mapowanie jako:" i poniższą tabelę zakładam, że chce on mapy z 'HttpMethodName' na' ProtocolOperation'. – Aivean

1

pójdę z oddzielenie logiki z wyliczenia. Twoje podejście to ciasne sprzężenie, mniej elastyczne. Z takich bibliotece przekształcić Metod są bardziej elastyczne

private static final Map<HttpMethodName , ProtocolOperation> mapping = new HashMap<>(); 
static { 
mapping .put(Create, POST); 
// rest of methods 
} 

a tu treść swojego sposobu odwzorowywania (używam Jdk8):

public static Optional<HttpMethodName > map(ProtocolOperation op){ 
if (!mapping.containsKey(op)){ 
    return Optional.empty(); 
} 
    return Optional.of(mapping.get(op)); 
} 

W skrócie: enum nie powinien mieć żadnej logiki biznesowej związanej z nimi, do mapowania i transformacji należy stosować metody zewnętrzne (utils).

UPDATE: Jak @Andreas słusznie zauważył, w tym konkretnym przykładzie, gdzie odwzorowanie jest ustalona i nie powinien być przedłużony, można zatrzymać się z jego sposobem inicjowania:

HttpMethodName(ProtocolOperation... ops) { 
    this.ops = Collections.unmodifiableList(Arrays.asList(ops)); 
} 
+1

Dlaczego sądzisz, że wyliczenie nie powinno zawierać mapowania? Dla mnie relacja między metodami HTTP i ich rolami jest ustalana za pomocą protokołu HTTP. Miałbyś rację, gdyby było kilka (powiedzmy więcej niż dwa) różnych mapowań z wyliczeniem "HttpMethodName", ale uważam, że jest to mało prawdopodobne. – Aivean

+0

Kilka lat później robiłem to samo. W tym przykładzie może istnieć potrzeba, ale generalnie mamy tendencję do trzymania kodu otwartego dla rozszerzeń. Mappingowanie w klasie narzędziowej pozwoli nam tworzyć oddzielne mapowania dla innych klientów/modułów itp. Ale muszę przyznać, że w tym przykładzie zachowanie listy enumów enodu enum może być dobre. – Beri

4

Utwórz mapowanie z ProtocolOperation na HttpMethodName, tak jak pokazano strzałkami, więc są to proste odniesienia, a nie listy.

Odwrotne mapowanie może być statyczną metodą, która iteruje 5 wartości wyliczonych dla dopasowań. Jest tylko 5 wartości, więc wyszukiwanie sekwencyjne jest odpowiednio szybkie, chyba że jest to coś, co robisz w bardzo ciasnej pętli, i jest to mało prawdopodobne w tym przypadku. Poza tym nie powinieneś kodować wydajności, dopóki profiler nie powie Ci, że masz problem.

śledzić mapowania następująco:

// From ProtocolOperation to HttpMethodName 
HttpMethodName method = ProtocolOperation.CREATE.getHttpMethodName(); 

// From HttpMethodName to ProtocolOperation 
ProtocolOperation op = ProtocolOperation.forMethod(HttpMethodName.GET); 

Realizacja:

public enum HttpMethodName 
{ 
    GET, POST, PUT, DELETE; 
} 
public enum ProtocolOperation { 
    CREATE (1, HttpMethodName.POST), 
    RETRIEVE(2, HttpMethodName.GET), 
    UPDATE (3, HttpMethodName.PUT), 
    DELETE (4, HttpMethodName.DELETE), 
    NOTIFY (5, HttpMethodName.POST); 

    private final int   operationId; 
    private final HttpMethodName httpMethodName; 

    private ProtocolOperation(int operationId, HttpMethodName httpMethodName) { 
     this.operationId = operationId; 
     this.httpMethodName = httpMethodName; 
    } 

    public int getOperationId() { 
     return this.operationId; 
    } 

    public HttpMethodName getHttpMethodName() { 
     return this.httpMethodName; 
    } 

    public static List<ProtocolOperation> forMethod(HttpMethodName httpMethodName) { 
     List<ProtocolOperation> ops = new ArrayList<>(); 
     for (ProtocolOperation op : values()) 
      if (op.httpMethodName == httpMethodName) 
       ops.add(op); 
     return ops; 
    } 
} 
+0

Wow, opublikowałem to samo rozwiązanie, co ty. To nie było zamierzone. Przebudźcie was :) – Aivean

+1

@Aivean Wielkie umysły myślą podobnie, huh. ;-) – Andreas