Java >> Java tutorial >  >> Tag >> switch

Er en switch-erklæring passende her, der tager en enum?

Der er en anden måde at undgå en switch case, som bruger en Map med en EnunMap implementering, som vil beholde Type som nøglen og værdien som Block implementering.

Når du har at gøre med Enum taster en EnumMap implementering anbefales i henhold til Javadoc:

En specialiseret kortimplementering til brug med enum type nøgler.

Denne repræsentation er ekstremt kompakt og effektiv.

Implementeringsnote:Alle grundlæggende operationer udføres i konstant tid. De er sandsynligvis (dog ikke garanteret) hurtigere end deres HashMap-modstykker.

Eksempelkode ved brug af en EnumMap i stedet for en switch hvor nøglen er Type enum-forekomst, og værdien er en Supplier<Block> (en konstruktørreference):

//During initialization
Map<Type, Supplier<Block>> registry =  new EnumMap<>(Type.class);
registry.put(Type.I, IBlock::new);
registry.put(Type.L, LBlock::new);
registry.put(Type.J, JBlock::new);
registry.put(Type.Z, ZBlock::new);
...

Så kan du hente den fra spawnBlock() ved at angive den korrekte Type og du ville få en ny forekomst af Block implementering hver gang på grund af Supplier.get() ring til sidst:

private void spawnBlock(Type type){
   currentBlock = this.registry.get(type).get();
}

Switch er bedre end at bruge if-else-sætninger. Efter min mening er det meget renere kode. Bare sammenlign den med den samme kode ved at bruge if-else:

private void spawnBlock(Type type){
    if(Type.I.equals(type)) {
        currentBlock = new IBlock();
    } else if(Type.L.equals(type)) {
        currentBlock = new LBlock();
    } else if(Type.J.equals(type)) {
        currentBlock = new JBlock();
    } else if(Type.Z.equals(type)) {
        currentBlock = new ZBlock();
    } else if(Type.S.equals(type)) {
        currentBlock = new SBlock();
    } else if(Type.T.equals(type)) {
        currentBlock = new TBlock();
    } else {
        currentBlock = new OBlock();
    }
}

Men ofte har du chancen for at bruge en anden tilgang end switch eller if-else. Bare tag et kig på de andre svar eller se på dette. Den forklarer, hvordan du kan forbedre din kode ved at bruge enum og sætte logikken direkte ind i enum.


Jeg er blevet bedt om at undgå at skifte udsagn, når det er muligt

Det er korrekt, men det du gør her måske være okay.

Pointen er:du ønsker ikke at have sådanne switch-udsagn over det hele . De er dårlige, fordi de kobler ting sammen. Og når du gentager det "samme" skiftemønster igen og igen, kan det blive til et vedligeholdelsesmareridt. Fordi ændringer i din enum-type ... betyder, at du bliver nødt til at se på hver switch for at afgøre, om den skal tilpasses.

Så når du er i stand til at skjule sådan skift fra det meste af din kode (ved kun at gøre det nogle få, ideelt set ét sted), er du okay.

Ikke desto mindre kunne en bedre tilgang være at erstatte hele denne switch-sætning med denne one-liner:

currentBlock = type.createBlock();

Med andre ord:hvorfor ikke lægge viden direkte ind i selve enum-klassen? Det har selvfølgelig andre implikationer, såsom en potentiel overtrædelse af enkelt ansvarsprincippet .

Men det føles ganske naturligt, at enummet, der angiver de forskellige typer af blokke, også giver et middel til at oprette sådanne blokke (i betragtning af at typen er den eneste information, der bestemmer, hvilken slags Block-underklasse du har brug for).

Og bemærk:du flytter dig ikke nødvendigvis kontakten ind i enummet. Du kan helt undgå at skifte her:

enum Type {
  I(IBlock::new), L(LBlock::new), ...;

  private Supplier<? extends Block> supplier;
  private Type(Supplier<? extends Block> supplier) {
    this.supplier = supplier;
  }

  public Block getBlock() {
    return supplier.get();
  }

(Jeg kørte ikke ovenstående gennem en compiler, så pas på med tastefejl )


Java tag