जावा में कथनों की लंबी सूची


101

क्षमा करें, मुझे इसका उत्तर देने वाला प्रश्न नहीं मिल रहा है, मैं लगभग निश्चित हूं कि किसी और ने इसे पहले उठाया है।

मेरी समस्या यह है कि मैं एम्बेडेड उपकरणों को चलाने के लिए कुछ सिस्टम लाइब्रेरी लिख रहा हूं। मेरे पास ऐसे आदेश हैं जो रेडियो प्रसारण पर इन उपकरणों को भेजे जा सकते हैं। यह केवल पाठ द्वारा किया जा सकता है। सिस्टम लाइब्रेरी के अंदर मेरे पास एक थ्रेड है जो इस तरह दिखने वाले कमांड को हैंडल करता है

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

समस्या यह है कि इसमें बहुत सारी कमांड हैं जो जल्दी से नियंत्रण से बाहर हो जाएगा। बाहर देखने के लिए भयानक, डिबग के लिए दर्दनाक और कुछ महीनों के समय में समझने के लिए दिमाग चकराता है।


16
बस एक टिप्पणी - मैं दृढ़ता से गैंग ऑफ़ फोर पैटर्न बुक लेने की सलाह दूंगा, या यदि आप पैटर्न के लिए नए हैं, तो जावा बुक में हेड फ़र्स्ट डिज़ाइन पैटर्न (जो कि बहुत आसान पढ़ा गया है और कई सामान्य पैटर्न का शानदार परिचय है। )। दोनों मूल्यवान संसाधन हैं, और दोनों ने मेरे बेकन को एक से अधिक बार बचाया है।
एपर्किंस

2
हाँ, वास्तव में मैंने उन्हें स्वामित्व दिया था लेकिन वे गायब हैं :) यही कारण है कि मुझे यकीन था कि मैं जो कर रहा था वह गलत था :) फिर भी एक सही समाधान नहीं मिल सका! शायद यह एक अच्छा गूगल स्थान प्राप्त करता है
स्टीव

2
यह सिर्फ सोमवार पैटर्न यहाँ है!
निक वेय्स

जवाबों:


171

कमांड पैटर्न का उपयोग करना :

public interface Command {
     void exec();
}

public class CommandA() implements Command {

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

// etc etc

फिर एक Map<String,Command>वस्तु का निर्माण करें और इसे Commandउदाहरणों से आबाद करें :

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

तो आप अपने जगह ले सकता है अगर / else if साथ श्रृंखला:

commandMap.get(value).exec();

संपादित करें

आप विशेष कमांड जैसे UnknownCommandया को भी जोड़ सकते हैं NullCommand, लेकिन CommandMapक्लाइंट के चेक को कम करने के लिए आपको इन कोने के मामलों को संभालना होगा।


1
... उपयुक्त जाँच के साथ कि commandMap.get () शून्य नहीं लौटता है :-)
ब्रायन एग्न्यू

3
बेशक, मैंने सादगी के लिए कुछ बॉयलरप्लेट कोड छोड़ दिया है
dfa

10
हाशपॅप के बजाय आप जावा एनुम का उपयोग कर सकते हैं, जो आपको एक भावपूर्ण मानचित्र के बजाय एक अच्छी तरह से निर्धारित कमांड देता है। आपके पास enum में एक getter हो सकता है: कमांड getCommand (); या यहां तक ​​कि क्रियान्वयन को लागू करें () एनम में एक अमूर्त विधि के रूप में, जिसे प्रत्येक उदाहरण लागू करता है (कमांड के रूप में एनम)।
JeeBee

2
यह एनम में सभी आदेशों को लागू करने के लिए मजबूर करेगा ... जो आदर्श होना चाहिए। एक इंटरफ़ेस के साथ आप डेकोरेटर पैटर्न भी लागू कर सकते हैं (उदाहरण के लिए DebugCommandDecorator, TraceCommandDecorator), एक बहुत अधिक लचीलापन अंतर्निहित है एक साधारण जावा इंटरफ़ेस में
dfa

5
ठीक है, छोटे और कभी नहीं बढ़ते आदेश के लिए एक एनम एक व्यवहार्य समाधान है।
dfa

12

मेरा सुझाव एनम और कमांड ऑब्जेक्ट के हल्के संयोजन का होगा। यह प्रभावी जावा के मद 30 में जोशुआ बलोच द्वारा अनुशंसित एक मुहावरा है।

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();
}

बेशक आप doCommand के मापदंडों को पारित कर सकते हैं या वापसी प्रकार कर सकते हैं।

यह समाधान वास्तव में उपयुक्त नहीं हो सकता है यदि डॉकॉम का कार्यान्वयन वास्तव में एनम प्रकार के लिए "फिट" नहीं होता है, जो कि है - हमेशा की तरह जब आपको ट्रेडऑफ़ करना पड़ता है - थोड़ा फजी।


7

आदेशों की एक सूची है:

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;
}

यदि आपके पास कुछ आदेशों से अधिक है, तो कमांड पैटर्न का उपयोग करके देखें, जैसा कि कहीं और उत्तर दिया गया है (हालांकि आप एनम को बनाए रख सकते हैं और एश के भीतर कार्यान्वयन वर्ग में कॉल को एम्बेड कर सकते हैं, बजाय एक हैशपॉप का उपयोग करके)। कृपया उदाहरण के लिए इस सवाल का जवाब एंड्रियास या जेन्स देखें।


5
आपके द्वारा जोड़े गए प्रत्येक नए आदेश के लिए, आपको स्विच को संपादित करने की आवश्यकता है: यह कोड खुले / बंद सिद्धांत का पालन नहीं करता है
dfa

इस पर निर्भर करता है कि कमांड कुछ हैं, या कई हैं, है ना? इसके अलावा यह साइट इन दिनों इतनी धीमी है कि एक उत्तर को संपादित करने में 5 प्रयास लगते हैं।
JeeBee

यह इष्टतम नहीं है देखें stackoverflow.com/questions/1199646/… इसे और अधिक इष्टतम कैसे करें।
एंड्रियास पीटरसन

हां, मेरी टिप्पणी के नीचे मैंने जो भी लिखा था उसे लागू करने के लिए समय देने के लिए धन्यवाद - कमांड पैटर्न के रूप में जावा एनम। यदि मैं अपनी पोस्ट को संपादित कर सकता हूं तो मैं इसका उल्लेख करूंगा, लेकिन यह साइट मर रही है।
JeeBee

मुझे लगता है कि यह प्रश्न एक स्विच स्टेटमेंट के लिए चिल्ला रहा है!
माइकल ब्राउन

7

एक इंटरफ़ेस को लागू करना जैसा कि सीधे और स्पष्ट रूप से dfa द्वारा प्रदर्शित किया जाता है, स्वच्छ और सुरुचिपूर्ण है (और "आधिकारिक तौर पर समर्थित तरीका")। इंटरफ़ेस अवधारणा के लिए यह क्या है।

C # में, हम प्रोग्रामर के लिए डेलीगेट्स का उपयोग कर सकते हैं जो c में फंक्शनलन पॉइंटर्स का उपयोग करना पसंद करते हैं, लेकिन DFA की तकनीक का उपयोग करने का तरीका है।

आपके पास एक सरणी भी हो सकती है

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

तब आप सूचकांक द्वारा एक कमांड निष्पादित कर सकते हैं

commands[7].exec();

डीएफए से चोरी, लेकिन एक इंटरफेस के बजाय एक सार आधार वर्ग होने। CmdKey को नोटिस करें जो बाद में उपयोग किया जाएगा। अनुभव से, मुझे पता चलता है कि अक्सर एक उपकरण के कमैंड में भी उप-क्षेत्र होते हैं।

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

इस प्रकार अपनी आज्ञाओं का निर्माण करें,

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

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

फिर आप एक महत्वपूर्ण-मूल्य जोड़ी चूसने की क्रिया प्रदान करके जेनेरिक हैशपैड या हैशटेबल का विस्तार कर सकते हैं:

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);
    }
  }
}

फिर अपने कमांड स्टोर का निर्माण करें:

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

अब आप उद्देश्यपूर्ण तरीके से नियंत्रण भेज सकते हैं

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

+1 का उल्लेख करने के लिए प्रतिनिधि। किसी भी .NET लोग इस सवाल को देखते हैं और एक-विधि इंटरफेस के साथ पागल हो जाते हैं। लेकिन वे वास्तव में फ़ंक्शन इंगित करने के लिए तुलनीय नहीं हैं। वे कमांड पैटर्न के एक भाषा-समर्थित संस्करण के करीब हैं।
डैनियल ईयरविकर

5

वैसे मैं सुझाव देता हूं कि कमांड ऑब्जेक्ट बनाएं और स्ट्रिंग के रूप में कुंजी का उपयोग करके उन्हें हैशमैप में डाल दें।


3

यहां तक ​​कि अगर मेरा मानना ​​है कि कमांड पैटर्न का दृष्टिकोण सर्वोत्तम pratices की ओर अधिक है और लंबे समय में बनाए रखने योग्य है, तो यहां आपके लिए एक लाइनर विकल्प है:

org.apache.commons.beanutils.MethodUtils.invokeMethod (यह, "doCommand" + मूल्य, नल);


2

मैं आमतौर पर इसे इस तरह से हल करने की कोशिश करता हूं:

public enum Command {

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

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

abstract void exec();
 }

इसके कई फायदे हैं:

1) निष्पादन को लागू किए बिना एक एनम जोड़ना संभव नहीं है। तो आप ए को याद नहीं करेंगे।

2) आपको इसे किसी कमांड मैप में जोड़ना भी नहीं पड़ेगा, इसलिए मैप बनाने के लिए कोई बॉयलरप्लेट कोड नहीं। बस सार विधि और इसके कार्यान्वयन। (जो यकीनन बॉयलरप्लेट भी है, लेकिन इसे कोई छोटा नहीं मिलेगा ..)

3) यदि आप हैशकोड की गणना या लुकअप कर रहे हैं तो लंबी सूची में से किसी भी व्यर्थ सीयूपी साइकल को बचाएंगे।

संपादित करें: यदि आपके पास स्रोत के रूप में नहीं बल्कि स्ट्रिंग्स हैं, Command.valueOf(mystr).exec()तो निष्पादन पद्धति को कॉल करने के लिए उपयोग करें। ध्यान दें कि आपको निष्पादक पर सार्वजनिक संशोधक का उपयोग करना चाहिए जिसे आप किसी अन्य पैकेज से कॉल करना चाहते हैं।


2

आप शायद मैप ऑफ कमांड्स का उपयोग करके सर्वश्रेष्ठ हैं।

लेकिन क्या आपके पास इनमें से एक ऐसा सेट है जो आपको नक्शों के भार से निपटने के लिए तैयार करता है। फिर यह एनमों के साथ करने लायक है।

आप स्विच का उपयोग किए बिना एनम के साथ कर सकते हैं (यदि आपको उदाहरण में गेटर्स की आवश्यकता नहीं है), यदि आप "मान" के लिए हल करने के लिए एनम को एक विधि जोड़ते हैं। तो आप बस कर सकते हैं:

अद्यतन: प्रत्येक कॉल पर पुनरावृत्ति से बचने के लिए स्थैतिक मानचित्र जोड़ा गया। बेशर्मी से इस जवाब पर चुटकी ली ।

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 द्वारा दिया गया उत्तर सबसे अच्छा समाधान है।

यदि आप जावा 8 का उपयोग कर रहे हैं और लैम्ब्डा का उपयोग करना चाहते हैं तो मैं कुछ स्निपेट प्रदान कर रहा हूँ !

मापदंडों के बिना कमान:

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();

(आप कमांड के बजाय एक रननेबल का उपयोग कर सकते हैं, लेकिन मैं इसे शब्दार्थिक रूप से सही नहीं मानता):

एक पैरामीटर के साथ कमांड:

यदि आप किसी ऐसे पैरामीटर की अपेक्षा करते हैं जो आप उपयोग कर सकते हैं 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);

ऊपर दिए गए उदाहरण में, doSomethingXएक विधि मौजूद है myObjजो paramतर्क के रूप में किसी भी वस्तु ( इस उदाहरण में नामित ) को लेती है।


1

यदि आपके पास कई imbricated 'if' स्टेटमेंट्स हैं, तो यह नियम इंजन का उपयोग करने के लिए एक पैटर्न है । उदाहरण के लिए, JBOSS ड्रोल्स देखें ।



0

यदि यह संभव था कि प्रक्रियाओं की एक सरणी (जिसे आप कमांड कहते हैं) जो उपयोगी होगी ..

लेकिन आप अपना कोड लिखने के लिए एक कार्यक्रम लिख सकते हैं। यह सब बहुत व्यवस्थित है अगर (मान = 'ए') कमांड (); अगर (........................ आदि)


0

मुझे यकीन नहीं है कि आपके विभिन्न आदेशों के व्यवहार के बीच कोई ओवरलैप है, लेकिन आप ज़िम्मेदारी के पैटर्न पर भी नज़र डाल सकते हैं, जो कुछ इनपुट मानों को संभालने के लिए कई कमांडों को अनुमति देकर अधिक लचीलापन प्रदान कर सकता है।


0

कमांड पैटर्न जाने का रास्ता है। यहाँ जावा 8 का उपयोग करके एक उदाहरण दिया गया है:

1. इंटरफ़ेस को परिभाषित करें:

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

2. विस्तार के प्रत्येक के साथ इंटरफेस को लागू करें:

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

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

तथा

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

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

और इसी तरह .....

3. ग्राहक को परिभाषित करें:

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. और यह नमूना परिणाम है:

  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

अगर यह बहुत सारी चीजें करता है, तो बहुत सारे कोड होंगे, आप वास्तव में उससे दूर नहीं हो सकते। बस इसका पालन करना आसान है, चर को बहुत सार्थक नाम दें, टिप्पणियाँ भी मदद कर सकती हैं ...

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.