java - जावा में बयानों की लंबी सूची




design-patterns command-pattern (13)

आप मानचित्र के कमांड का उपयोग कर शायद सबसे अच्छे हैं।

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

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

अद्यतन: प्रत्येक कॉल पर पुनरावृत्ति से बचने के लिए स्थिर स्थिर मानचित्र। बेमिसाल इस जवाब से चुरा लिया।

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

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

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

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

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


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

public enum Command {

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

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

abstract void exec();
 }

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

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

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

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

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


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

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

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

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


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

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


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

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

तो यदि आप श्रृंखला के साथ हैं तो आप अपने / अन्य को प्रतिस्थापित कर सकते हैं:

commandMap.get(value).exec();

संपादित करें

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


वैसे एक कमांड पैटर्न है लेकिन आप जो भी कर रहे हैं उसके लिए यह अधिक हो सकता है। KISS याद रखें।


मेरा सुझाव enum और कमांड ऑब्जेक्ट का हल्का संयोजन होगा। प्रभावशाली जावा के आइटम 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();
}

बेशक आप कमांड करने के लिए पैरामीटर पास कर सकते हैं या वापसी प्रकार हैं।

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


आदेशों का एक enum है:

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

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


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


@ डीएफए द्वारा प्रदान किया गया उत्तर मेरी राय में सबसे अच्छा समाधान है।

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

पैरामीटर के बिना कमांड:

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 उपयोग कर सकते हैं। 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 नामित) लेता है।


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


जावा हमेशा पास-दर-मूल्य होता है । दुर्भाग्य से, उन्होंने किसी ऑब्जेक्ट का स्थान "संदर्भ" कहने का निर्णय लिया। जब हम किसी ऑब्जेक्ट का मान पास करते हैं, तो हम इसका संदर्भ पारित कर रहे हैं। यह शुरुआती लोगों को भ्रमित कर रहा है।

यह इस प्रकार चलता है:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true
    aDog.getName().equals("Fifi"); // false 
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

aDog.getName() ऊपर दिए गए उदाहरण में अभी भी "Max" वापस आ जाएगा। ऑब्जेक्ट संदर्भ मूल्य द्वारा पारित किया गया है क्योंकि main से मूल्य aDog फ़ंक्शन foo में Dog "Fifi" साथ नहीं बदला जाता है। यदि इसे संदर्भ द्वारा पारित किया गया था, तो main में aDog.getName() कॉल करने के बाद कॉल के बाद "Fifi" लौटाएगा।

इसी तरह:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

उपर्युक्त उदाहरण में, Fifi foo(aDog) पर कॉल करने के बाद कुत्ते का नाम है क्योंकि ऑब्जेक्ट का नाम foo(...) अंदर सेट किया गया था। d पर किए जाने वाले किसी भी ऑपरेशन ऐसे हैं कि, सभी व्यावहारिक उद्देश्यों के लिए, वे एक Dog पर ही किए जाते हैं (सिवाय इसके कि जब d को बदल दिया जाता है तो d = new Dog("Boxer") जैसे विभिन्न Dog उदाहरण को इंगित किया जाता है।







java design-patterns command-pattern