Java'daki if ifadelerinin uzun listesi


101

Maalesef bunu yanıtlayan bir soru bulamıyorum, daha önce başka birinin sorduğundan neredeyse eminim.

Benim sorunum, gömülü cihazları çalıştırmak için bazı sistem kitaplıkları yazıyorum. Bu cihazlara radyo yayınları üzerinden gönderilebilecek komutlarım var. Bu yalnızca metin yoluyla yapılabilir. sistem kitaplıklarının içinde buna benzeyen komutları işleyen bir iş parçacığım var

if (value.equals("A")) { doCommandA() }
else if (value.equals("B")) { doCommandB() } 
else if etc. 

Sorun şu ki, birçok komutun hızlı bir şekilde kontrolden çıkan bir şeye dönmesi. Dikkat etmesi korkunç, hata ayıklaması acı verici ve birkaç ay içinde anlamak akıl almaz.


16
Sadece bir yorum - Gang of Four kalıpları kitabını veya kalıplar konusunda yeniyseniz, Java'da Baş İlk Tasarım Kalıpları kitabını (oldukça kolay okunan ve bir dizi yaygın modele harika bir giriş olan) almanızı şiddetle tavsiye ederim. ). Her ikisi de değerli kaynaklar ve her ikisi de pastırmamı bir kereden fazla kurtardı.
aperkins

2
Evet aslında onlara sahiptim ama eksikler :) Bu yüzden yaptığım şeyin yanlış olduğundan emindim :) Yine de doğru bir çözüm bulamadım! Belki bu güzel bir Google pozisyonu alır
Steve

2
Burada sadece Komuta Modeli Pazartesi var!
Nick Veys

Yanıtlar:


171

Komut kalıbını kullanarak :

public interface Command {
     void exec();
}

public class CommandA() implements Command {

     void exec() {
          // ... 
     }
}

// etc etc

daha sonra bir Map<String,Command>nesne oluşturun ve onu Commandörneklerle doldurun:

commandMap.put("A", new CommandA());
commandMap.put("B", new CommandB());

if / else if zincirinizi şu şekilde değiştirebilirsiniz :

commandMap.get(value).exec();

DÜZENLE

ayrıca UnknownCommandveya gibi özel komutlar da ekleyebilirsiniz NullCommand, ancak CommandMapmüşterinin kontrollerini en aza indirmek için bu köşe durumlarını yöneten bir şeye ihtiyacınız vardır .


1
... commandMap.get () 'ın null döndürmediğini uygun şekilde kontrol ederek :-)
Brian Agnew

3
Tabii ki, basitlik uğruna bazı standart kodları
atladım

10
Bir HashMap yerine, size duygusal bir harita yerine iyi tanımlanmış bir komut kümesi sağlayan bir Java numaralandırması kullanabilirsiniz. Numaralandırmada bir alıcıya sahip olabilirsiniz: Command getCommand (); hatta exec () 'i enumda soyut bir yöntem olarak uygulayabilir, her örneğin uyguladığı (komut olarak enum).
JeeBee

2
bu, ideal olmaktan çok uzak olan enum'daki tüm komutları uygulamaya zorlar. Bir arayüzle Dekoratör modelini de uygulayabilirsiniz (örn. DebugCommandDecorator, TraceCommandDecorator), basit bir Java arayüzünde yerleşik çok daha fazla esneklik vardır
dfa

5
Tamam, küçük ve hiç büyümeyen komutlar için bir numaralandırma uygun bir çözümdür.
dfa

12

Benim önerim, enum ve Command nesnesinin bir tür hafif birleşimi olacaktır. Bu, Etkili Java'nın 30. Maddesinde Joshua Bloch tarafından önerilen bir deyimdir.

public enum Command{
  A{public void doCommand(){
      // Implementation for A
    }
  },
  B{public void doCommand(){
      // Implementation for B
    }
  },
  C{public void doCommand(){
      // Implementation for C
    }
  };
  public abstract void doCommand();
}

Elbette parametreleri doCommand'e iletebilir veya dönüş türlerine sahip olabilirsiniz.

DoCommand uygulamalarının sıralama türüne gerçekten "uymuyorsa" bu çözüm gerçekten uygun olmayabilir, ki bu - her zamanki gibi bir değiş tokuş yapmanız gerektiğinde - biraz bulanıktır.


7

Bir komut listesine sahip olun:

public enum Commands { A, B, C; }
...

Command command = Commands.valueOf(value);

switch (command) {
    case A: doCommandA(); break;
    case B: doCommandB(); break;
    case C: doCommandC(); break;
}

Birkaç komutunuz varsa, başka bir yerde yanıtlandığı gibi Komut desenini kullanmaya bakın (ancak bir HashMap kullanmak yerine numaralandırmayı koruyabilir ve enum içindeki uygulama sınıfına çağrıyı gömebilirsiniz). Örnek için lütfen Andreas veya jens'in bu soruya verdiği cevaba bakınız.


5
Eklediğiniz her yeni komut için anahtarı düzenlemeniz gerekir: bu kod açık / kapalı prensibini izlemiyor
dfa

Komutların az mı yoksa çok mu olduğuna bağlı, değil mi? Ayrıca bu site bu günlerde o kadar yavaş ki bir cevabı düzenlemek için 5 deneme gerekiyor.
JeeBee

bu optimal değildir, bunun nasıl daha iyi yapılacağını öğrenmek için stackoverflow.com/questions/1199646/… adresine bakın .
Andreas Petersson

Evet, yorumumun altında yazdıklarımı - Komut Kalıbı olarak Java Enum'u uygulamak için zaman ayırdığınız için teşekkür ederiz. Yazımı düzenleyebilseydim bundan söz ederdim, ama bu site ölüyor.
JeeBee

Sanırım bu soru bir Switch açıklaması için çığlık atıyor!
Michael Brown

7

Basit ve açık bir şekilde dfa tarafından gösterildiği gibi bir arayüz uygulamak temiz ve zariftir (ve "resmi olarak" desteklenen bir yoldur). Arayüz konseptinin anlamı budur.

C # 'da, c'de işlev işaretçileri kullanmayı seven programcılar için temsilciler kullanabiliriz, ancak DFA'nın tekniği kullanmanın yoludur.

Sen de bir diziye sahip olabilirsin

Command[] commands =
{
  new CommandA(), new CommandB(), new CommandC(), ...
}

Sonra dizine göre bir komut çalıştırabilirsiniz.

commands[7].exec();

DFA'lardan intihal yapma, ancak arayüz yerine soyut bir temel sınıfa sahip olma. Daha sonra kullanılacak olan cmdKey'e dikkat edin. Tecrübe ile, bir ekipman komutanının sıklıkla alt komutları olduğunu fark ediyorum.

abstract public class Command()
{
  abstract public byte exec(String subCmd);
  public String cmdKey;
  public String subCmd;
}

Komutlarınızı bu şekilde oluşturun,

public class CommandA
extends Command
{
  public CommandA(String subCmd)
  {
    this.cmdKey = "A";
    this.subCmd = subCmd;
  }

  public byte exec()
  {
    sendWhatever(...);
    byte status = receiveWhatever(...);
    return status;
  }
}

Daha sonra bir anahtar-değer çifti emme işlevi sağlayarak genel HashMap veya HashTable'ı genişletebilirsiniz:

public class CommandHash<String, Command>
extends HashMap<String, Command>
(
  public CommandHash<String, Command>(Command[] commands)
  {
    this.commandSucker(Command[] commands);
  }
  public commandSucker(Command[] commands)
  {
    for(Command cmd : commands)
    {
      this.put(cmd.cmdKey, cmd);
    }
  }
}

Ardından komut deponuzu oluşturun:

CommandHash commands =
  new CommandHash(
  {
    new CommandA("asdf"),
    new CommandA("qwerty"),
    new CommandB(null),
    new CommandC("hello dolly"),
    ...
  });

Şimdi objektif olarak kontroller gönderebilirsiniz

commands.get("A").exec();
commands.get(condition).exec();

Herhangi bir .NET kullanıcısının bu soruyu görmesi ve tek yöntemli arayüzlerle çıldırması ihtimaline karşı temsilcilerden bahsetmek için +1. Ama gerçekten işlev işaretçileriyle karşılaştırılamazlar. Komut modelinin dil destekli sürümüne daha yakındırlar.
Daniel Earwicker

5

Pekala, komut nesneleri oluşturmanızı ve String'i Key olarak kullanarak bir hashmap'e koymanızı öneririm.


3

Komut modeli yaklaşımının en iyi uygulamalara yönelik olduğuna ve uzun vadede sürdürülebileceğine inansam bile, işte sizin için bir seçenek:

org.apache.commons.beanutils.MethodUtils.invokeMethod (bu, "doCommand" + değer, null);


2

genellikle bu şekilde çözmeye çalışırım:

public enum Command {

A {void exec() {
     doCommandA();
}},

B {void exec() {
    doCommandB();
}};

abstract void exec();
 }

bunun birçok avantajı vardır:

1) exec uygulamadan bir enum eklemek mümkün değildir. böylece bir A'yı kaçırmayacaksınız.

2) herhangi bir komut haritasına eklemeniz bile gerekmeyecek, bu nedenle haritayı oluşturmak için standart kod yok. sadece soyut yöntem ve uygulamaları. (tartışmaya açık bir şekilde standart metindir, ancak daha kısalmayacaktır ..)

3) uzun bir if's listesinden geçerek veya hashCode'ları hesaplayarak ve arama yaparak boşa harcanan cpu döngülerini kaydedersiniz.

edit: kaynak olarak numaralandırmalarınız yoksa dizeleriniz yoksa, sadece Command.valueOf(mystr).exec()exec yöntemini çağırmak için kullanın . Başka bir paketten çağırmak istiyorsanız exec üzerinde public değiştiriciyi kullanmanız gerektiğini unutmayın.


2

Muhtemelen en iyisi Komutlar Haritası kullanmaktır.

Ancak, sizi ele geçirecek bir dizi Haritalarınız var mı? O zaman Enums ile yapmaya bakmaya değer.

Bunu anahtar kullanmadan bir Enum ile yapabilirsiniz (muhtemelen örnekteki alıcılara ihtiyacınız yoktur), "değer" için Enum'a bir yöntem eklerseniz. O zaman şunları yapabilirsiniz:

Güncelleme: Her aramada yinelemeyi önlemek için statik harita eklendi. Utanmazca bu cevaptan koparıldı .

Commands.getCommand(value).exec();

public interface Command {
    void exec();
}

public enum Commands {
    A("foo", new Command(){public void exec(){
        System.out.println(A.getValue());
    }}),
    B("bar", new Command(){public void exec(){
        System.out.println(B.getValue());
    }}),
    C("barry", new Command(){public void exec(){
        System.out.println(C.getValue());
    }});

    private String value;
    private Command command;
    private static Map<String, Commands> commandsMap;

    static {
        commandsMap = new HashMap<String, Commands>();
        for (Commands c : Commands.values()) {
            commandsMap.put(c.getValue(), c);    
        }
    }

    Commands(String value, Command command) {
        this.value= value;
        this.command = command;
    }

    public String getValue() {
        return value;
    }

    public Command getCommand() {
        return command;
    }

    public static Command getCommand(String value) {
        if(!commandsMap.containsKey(value)) {
            throw new RuntimeException("value not found:" + value);
        }
        return commandsMap.get(value).getCommand();
    }
}

2

@Dfa tarafından sağlanan cevap bence en iyi çözüm.

Java 8 kullanıyorsanız ve Lambdas kullanmak istemeniz ihtimaline karşı sadece bazı parçacıklar sağlıyorum!

Parametresiz komut:

Map<String, Command> commands = new HashMap<String, Command>();
commands.put("A", () -> System.out.println("COMMAND A"));
commands.put("B", () -> System.out.println("COMMAND B"));
commands.put("C", () -> System.out.println("COMMAND C"));
commands.get(value).exec();

(Komut yerine Runnable kullanabilirsiniz, ancak bunun anlamsal olarak doğru olduğunu düşünmüyorum):

Tek parametreli komut:

Bir parametre beklemeniz durumunda kullanabileceğiniz java.util.function.Consumer:

Map<String, Consumer<Object>> commands = new HashMap<String, Consumer<Object>>();
commands.put("A", myObj::doSomethingA);
commands.put("B", myObj::doSomethingB);
commands.put("C", myObj::doSomethingC);
commands.get(value).accept(param);

Yukarıdaki örnekte, 's sınıfında bulunan ve herhangi bir Object'i ( bu örnekte adlandırılmıştır ) bağımsız değişken olarak doSomethingXalan bir yöntemdir .myObjparam




0

bir dizi yordamın (komut olarak adlandırdığınız) olması mümkün olsaydı, bu yararlı olurdu ..

ama kodunuzu yazmak için bir program yazabilirsiniz. Hepsi çok sistematik if (value = 'A') commandA (); else if (........................ vb.)


0

Çeşitli komutlarınızın davranışları arasında herhangi bir örtüşme olup olmadığından emin değilim, ancak aynı zamanda, birden fazla komutun bazı girdi değerlerini işlemesine izin vererek daha fazla esneklik sağlayabilecek Sorumluluk Zinciri modeline de bir göz atmak isteyebilirsiniz .


0

Komut kalıbı gitmenin yoludur. İşte java 8 kullanan bir örnek:

1. Arayüzü tanımlayın:

public interface ExtensionHandler {
  boolean isMatched(String fileName);
  String handle(String fileName);
}

2. Arayüzü uzantıların her biriyle uygulayın:

public class PdfHandler implements ExtensionHandler {
  @Override
  public boolean isMatched(String fileName) {
    return fileName.endsWith(".pdf");
  }

  @Override
  public String handle(String fileName) {
    return "application/pdf";
  }
}

ve

public class TxtHandler implements ExtensionHandler {
  @Override public boolean isMatched(String fileName) {
    return fileName.endsWith(".txt");
  }

  @Override public String handle(String fileName) {
    return "txt/plain";
  }
}

ve bunun gibi .....

3. Müşteriyi Tanımlayın:

public class MimeTypeGetter {
  private List<ExtensionHandler> extensionHandlers;
  private ExtensionHandler plainTextHandler;

  public MimeTypeGetter() {
    extensionHandlers = new ArrayList<>();

    extensionHandlers.add(new PdfHandler());
    extensionHandlers.add(new DocHandler());
    extensionHandlers.add(new XlsHandler());

    // and so on

    plainTextHandler = new PlainTextHandler();
    extensionHandlers.add(plainTextHandler);
  }

  public String getMimeType(String fileExtension) {
    return extensionHandlers.stream()
      .filter(handler -> handler.isMatched(fileExtension))
      .findFirst()
      .orElse(plainTextHandler)
      .handle(fileExtension);
  }
}

4. Ve bu örnek sonuçtur:

  public static void main(String[] args) {
    MimeTypeGetter mimeTypeGetter = new MimeTypeGetter();

    System.out.println(mimeTypeGetter.getMimeType("test.pdf")); // application/pdf
    System.out.println(mimeTypeGetter.getMimeType("hello.txt")); // txt/plain
    System.out.println(mimeTypeGetter.getMimeType("my presentation.ppt")); // "application/vnd.ms-powerpoint"
  }

-1

Bir çok şey yaparsa, o zaman çok fazla kod olacaktır, bundan gerçekten kaçamazsınız. Sadece takip etmeyi kolaylaştırın, değişkenlere çok anlamlı isimler verin, yorumlar da yardımcı olabilir ...

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.