2012-01-09 15 views
5

मेरे पास डेटाबेस में कतारबद्ध नौकरियों की एक सूची है जिसे मुझे डेटाबेस से पढ़ने और थ्रेडिंग का उपयोग करके समानांतर में निष्पादित करने की आवश्यकता है और मेरे पास प्रत्येक को निष्पादित करने के लिए कमांड कक्षाओं की एक सूची है उन सभी नौकरियों में एक आम इंटरफ़ेस (कमांड पैटर्न) लागू करना। लेकिन जब मैं डेटाबेस से लंबित नौकरियों को पुनः प्राप्त, मैं इस तरह हर काम कुछ के लिए सही आदेश वस्तु का दृष्टांत को (एक कारखाने वर्ग में)कतारबद्ध नौकरियों को निष्पादित करने के लिए कमांड और फैक्ट्री डिज़ाइन पैटर्न का उपयोग करके

ICommand command; 
switch (jobCode) 
{ 
    case "A": 
    command = new CommandA(); 
    break; 
    case "B": 
    command = new CommandB(); 
    break; 
    case "C": 
    command = new CommandC(); 
    break; 
} 

command.Execute(); 

वहाँ सही आदेश वस्तु बनाने के लिए एक बेहतर तरीका है की आवश्यकता होगी उपरोक्त की तरह एक बड़ा स्विच स्टेटमेंट का उपयोग किए बिना? या कतारबद्ध नौकरियों को निष्पादित करने के लिए कोई अन्य पैटर्न है?

समाधान: इस तरह हल किया गया (चयनित उत्तर के आधार पर)। यह आदेश वस्तुओं की आलसी तात्कालिकता करेगा।

public class CommandFactory 
{ 
    private readonly IDictionary<string, Func<ICommand>> _commands; 

    public CommandFactory() 
    { 
     _commands = new Dictionary<string, Func<ICommand>> 
         { 
          {"A",() => new CommandA()}, 
          {"B",() => new CommandB()}, 
          {"C",() => new CommandC()} 
         }; 
    } 

    public ICommand GetCommand(string jobKey) 
    { 
     Func<ICommand> command; 
     _commands.TryGetValue(jobKey.ToUpper(), out command); 
     return command(); 
    } 
}  

Client: 

     var factory = new CommandFactory(); 
     var command = factory.GetCommand(jobKey); 
     command.Execute(); 
+0

यह आपके कारखाने में होना होगा अपने आदेश के सभी पर विचार त्रुटिपूर्ण लगता है। – KingOfHypocrites

उत्तर

12

अधिकांश सी # आदेश पैटर्न कार्यान्वयन लगभग समान ही । इन कार्यान्वयन आमतौर पर एक ICommand इंटरफ़ेस का उपयोग:

public interface ICommand 
{ 
    void Execute(); 
} 

और फिर सभी आदेश कक्षाएं इंटरफ़ेस को लागू करने के लिए मजबूर कर रहे हैं। मुझे इस समाधान के साथ कोई समस्या नहीं है, लेकिन व्यक्तिगत रूप से मुझे बहुत से वर्ग बनाने की इच्छा नहीं है और मैं इसके बजाय .NET प्रतिनिधियों का उपयोग करना पसंद करता हूं (जावा में कोई प्रतिनिधि नहीं हैं)। कार्रवाई प्रतिनिधि आमतौर पर काम कर देता है, तो केवल एक ही विधि संदर्भ की जरूरत है:

public class Prog 
{ 
    public Prog() 
    { 
     var factory = new CommandFactory(); 
     factory.Register("A",() => new A().DoA);    
     factory.Register("B",() => new B().DoB); 
     factory.Register("C", DoStuff); 

     factory.Execute("A"); 
    } 

    public static void DoStuff() 
    { 
    } 
} 

public class CommandFactory 
{ 
    private readonly IDictionary<string, Action> _commands;  

    public void Register(string commandName, Action action) 
    { 
    _commands.Add(commandName, action); 
    } 

    public Action GetCommand(string commandName) 
    { 
     _commands[commandName]; 
    } 

    public void Execute(string commandName) 
    { 
     GetCommand(commandName)(); 
    } 
} 
public class A 
{ 
    public void DoA() 
    { 
    } 
} 

public class B 
{ 
    public void DoB() 
    { 
    } 
} 

अपने आदेश इंटरफ़ेस एक से अधिक तरीकों की तरह की जरूरत है:

public interface ICommand 
{ 
    void Execute(); 
    void Undo(); 
} 

आप इस प्रकार का आवरण वर्ग का उपयोग कर सकते हैं:

public class Command 
{ 
    public Command(Action execute, Action undo) 
    { 
     Execute = execute; 
     Undo = undo; 
    } 

    public Action Execute { get; protected set; } 
    public Action Undo { get; protected set; } 
} 

या (यह कोई फर्क नहीं पड़ता जो एक)

public class Command 
{ 
    private readonly Action _execute; 
    private readonly Action _undo; 

    public Command(Action execute, Action undo) 
    { 
     _execute = execute; 
     _undo = undo; 
    } 

    public void Execute() 
    { 
     _execute(); 
    } 

    public void Undo() 
    { 
     _undo(); 
    } 
} 

(यदि आपके पास पहले से ही इसका उपयोग करने वाली विरासत सामग्री है तो यह भी आईसीओएमएंड को कार्यान्वित कर सकता है।आप इंटरफ़ेस का उपयोग करते हैं कारखाने इंटरफेस के बजाय कमान वर्ग)

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

public class Prog2 
{ 
    public Prog2() 
    { 
     var factory = new CommandFactory2(); 
     factory.Register("A", new Lazy<Command>(
      ()=> 
       { 
        var a = new A(); 
        return new Command(a.DoA, a.UndoA); 
       })); 

     factory.Register("B", new Lazy<Command>(
      () => 
      { 
       var c = new B(); 
       return new Command(c.DoB, c.DoB); 
      })); 

     factory.Register("C", new Lazy<Command>(
      () => new Command(DoStuff, UndoStuff))); 

     factory.Execute("A"); 
    } 

    public static void DoStuff() 
    { 
    } 

    public static void UndoStuff() 
    { 
    } 
} 

public class CommandFactory2 
{ 
    private readonly IDictionary<string, Lazy<Command>> _commands; 

    public void Register(string commandName, Lazy<Command> lazyCommand) 
    { 
     _commands.Add(commandName, lazyCommand); 
    } 

    public void Register(string commandName, Action execute, Action undo) 
    { 
     _commands.Add(commandName, new Lazy<Command>(() => new Command(execute, undo))); 
    } 

    public Command GetCommand(string commandName) 
    { 
     return _commands[commandName].Value; 
    } 

    public void Execute(string commandName) 
    { 
     GetCommand(commandName).Execute(); 
    } 

    public void Undo(string commandName) 
    { 
     GetCommand(commandName).Undo(); 
    } 
} 


public class A 
{ 
    public void DoA() 
    { 
    } 

    public void UndoA() 
    { 
    } 
} 

public class B 
{ 
    public void DoB() 
    { 
    } 

    public void UndoB() 
    { 
    } 
} 

आप देख सकते हैं इंटरफ़ेस को लागू करने के लिए आप एक से अधिक विधि (निष्पादित, पूर्ववत, आदि) है, भले ही कोई जरूरत नहीं है। कृपया ध्यान दें कि निष्पादन और पूर्ववत विधियां विभिन्न वर्गों से संबंधित हो सकती हैं। आप अपने कोड को और अधिक प्राकृतिक महसूस करने के तरीके के लिए स्वतंत्र हैं और अभी भी कमांड पैटर्न का उपयोग कर सकते हैं।

+0

आपके प्रदत्त उदाहरण में डिज़ाइन लचीलापन और पुन: प्रयोज्यता की कमी है, आपका प्रदत्त उदाहरण उपयोगकर्ता को हार्ड कोडेड क्लास जैसे अधिक बनाने के लिए प्रेरित कर रहा है .. मैं प्रीफेर @ स्लेड का उदाहरण यह बेहतर है। –

+0

कृपया बताएं कि इसमें डिज़ाइन लचीलापन और पुन: प्रयोज्यता की कमी क्यों है? आप शब्दकोश में कोई भी विधि जोड़ सकते हैं जो प्रतिनिधि हस्ताक्षर को संतुष्ट करता है। "हार्ड कोडेड क्लास की तरह" से आपका क्या मतलब है? –

+0

फैक्ट्री। रजिस्ट्रार ("सी" उन हार्ड कोडिंग में से एक है जो डिज़ाइन में एक बड़ी गड़बड़ी है। इसलिए हार्ड कोड नहीं है। – Zenwalker

1

आप अपनी नौकरी पूछ यह प्रदान करने के लिए विचार कर सकते हैं खुद ICommand है: jobCode स्विच ऑन करने के बजाय फिर

interface IJob 
{ 
    ICommand Command { get; } 
} 

public class JobA : IJob 
{ 
    private readonly ICommand _command = new CommandA(); 
    public ICommand Command { get { return _command; } } 
} 

, तो आप सिर्फ कर सकता है:

job.Command.Execute(); 
+1

यकीन नहीं है कि यह मदद करेगा। कमांड ऑब्जेक्ट बनाने के लिए स्विच करने के बजाय, मुझे इसका उपयोग करके सही नौकरी ऑब्जेक्ट बनाने के लिए एक स्विच की आवश्यकता है। बिंदु जहां काम instantiated है आप जानते हैं कि आप चाहते हैं और इसलिए जो भी आदेश काम के प्रकार पर - – RKP

+0

@RKP मैं अपने 'jobCode' एक नौकरी से आएगा ग्रहण किया। –

+0

बिंदु फिर से चर्चा जो ICommand के बजाय अब वह IJob पर गौर करने की जरूरत है :) –

4

आप एक इस्तेमाल कर सकते हैं Dictionary प्रासंगिक ICommand कार्यान्वयन के लिए पत्र/वर्ण को मैप करने के लिए। की तरह कुछ:

public class CommandFactory 
{ 
    private readonly Dictionary<string, ICommand> mCommands = new Dictionary<string,ICommand>(StringComparer.OrdinalIgnoreCase); 

    public void RegisterCommand<TCommand>(string commandKey) where TCommand : ICommand, new() 
    { 
     // Instantiate the command 
     ICommand command = new TCommand(); 

     // Add to the collection 
     mCommands.Add(commandKey, command); 
    } 

    public void ExecuteCommand(string commandKey) 
    { 
     // See if the command exists 
     ICommand command; 
     if (!mCommands.TryGetValue(commandKey, out command)) 
     { 
      // TODO: Handle invalid command key 
     } 

     // Execute the command 
     command.Execute(); 
    } 
} 

इस का उपयोग करना, आप कमांड प्रकार रजिस्टर और उन्हें string आधारित चाबियाँ करने के लिए नक्शे और उन्हें instantiated और सामान्य रूप से अधिक निष्पादित करने की अनुमति दे सकते हैं। जब आप पहली बार उपयोग किए जाते हैं तो कमांड प्रकारों को तुरंत चालू करके आप प्रदर्शन में सुधार कर सकते हैं।

संपादित

अपनी टिप्पणी के जवाब में, केवल दृष्टांत के लिए जब क्रियान्वित करने के लिए, आप की तरह कुछ कर सकता है: एक जावा कार्यान्वयन के रूप में

public class CommandDetails<T> where T : ICommand, new() 
{ 
    private ICommand mCommand; 

    public ICommand GetCommand() 
    { 
     if (/* Determine if the command has been instantiated */) 
     { 
      // Instantiate the command 
      mCommand = new T(); 
     } 

     return mCommand; 
    } 
} 

public void ExecuteCommand(...) 
{ 
    // See if the command exists 
    CommandDetails details; 
    // ... 

    // Get the command 
    // Note: If we haven't got the command yet, this will instantiate it for us. 
    ICommand command = details.GetCommand(); 

    // ... 
} 
+0

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

+0

@RKP मेरा विस्तारित उत्तर देखें। –

+0

लेकिन मैं इसे तुरंत चालू किए बिना कमांड को कैसे पंजीकृत कर सकता हूं (शब्दकोश में कमांड जोड़ें)? एक विकल्प दूसरे प्रतिनिधियों में निर्दिष्ट प्रतिनिधियों का उपयोग करना है। जानना चाहते हैं कि क्या कोई और समाधान है या नहीं। – RKP