कमांड पैटर्न डिजाइन


11

मेरे पास कमांड पैटर्न का यह पुराना कार्यान्वयन है। यह सभी DIOperation कार्यान्वयन के माध्यम से एक संदर्भ पारित करने का एक प्रकार है , लेकिन मुझे बाद में पता चला, सीखने और सीखने की प्रक्रिया में (जो कभी नहीं रुकता), यह इष्टतम नहीं है। मुझे भी लगता है कि यहां "दौरा" वास्तव में फिट नहीं है और सिर्फ भ्रमित करता है।

मैं वास्तव में अपने कोड को फिर से भरने के बारे में सोच रहा हूं, इसलिए भी कि एक कमांड को दूसरों के बारे में कुछ नहीं पता होना चाहिए और फिलहाल वे सभी एक ही कुंजी-मूल्य वाले जोड़े साझा करते हैं। यह वास्तव में कठिन है कि कौन सा वर्ग किस कुंजी-मूल्य का मालिक है, कभी-कभी डुप्लिकेट चर का नेतृत्व करता है।

उपयोग के मामले का एक उदाहरण: मान लें कि CommandB को UserName की आवश्यकता है जो CommandA द्वारा निर्धारित है । क्या CommandA ने UserNameForCommandB = John को कुंजी सेट करनी चाहिए ? या उन्हें एक सामान्य उपयोगकर्ता नाम = जॉन कुंजी-मूल्य साझा करना चाहिए ? क्या होगा यदि UserName का उपयोग किसी तीसरे कमांड द्वारा किया जाता है?

मैं इस डिज़ाइन को कैसे सुधार सकता हूं? धन्यवाद!

class DIParameters {
public:
   /**
    * Parameter setter.
    */
    virtual void setParameter(std::string key, std::string value) = 0;
    /**
    * Parameter getter.
    */
    virtual std::string getParameter(std::string key) const = 0;

    virtual ~DIParameters() = 0;
};

class DIOperation {
public:
    /**
     * Visit before performing execution.
     */
    virtual void visitBefore(DIParameters& visitee) = 0;
    /**
     * Perform.
     */
    virtual int perform() = 0;
    /**
     * Visit after performing execution.
     */
    virtual void visitAfter(DIParameters& visitee) = 0;

    virtual ~DIOperation() = 0;
};

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

1
1. क्यों एक अलग आगंतुक के माध्यम से मापदंडों को पारित? प्रदर्शन के तर्क के रूप में एक संदर्भ पारित करने में क्या गलत है? 2. संदर्भ कमांड के 'सामान्य' भाग के लिए है (उदाहरण। वर्तमान सत्र / दस्तावेज़)। ऑपरेशन के कंस्ट्रक्टर के माध्यम से सभी ऑपरेशन-विशिष्ट मापदंडों को बेहतर तरीके से पारित किया जाता है।
क्रिस वान बाल

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

क्या आप मेरे आदेशों के बीच की घटनाओं को सही मानते हैं? क्या आप अपने कुंजी-मान वहां डालेंगे (एंड्रॉइड पार्सल के साथ क्या करता है)? क्या यह इस अर्थ में समान होगा कि कमांडए मुख्य-मूल्य वाले जोड़े के साथ एक ईवेंट का निर्माण करे, जिसे कमांडबी स्वीकार करता है?
एंड्रिया रिचियार्डी

जवाबों:


2

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

आपके दृष्टिकोण की समस्याएं:

क्या आप चाहते हैं कि अन्य थ्रेड / कमांड आपके मापदंडों को बदलने के performलिए चल रहे हैं?

क्या आप चाहते हैं visitBeforeऔर visitAfterएक ही Commandवस्तु को विभिन्न DIParameterवस्तुओं के साथ बुलाया जाना है ?

क्या आप चाहते हैं कि कोई आपके कमांड को पैरामीटर खिलाए, जिसके बारे में कमांड्स को कोई जानकारी नहीं है?

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

परिणामों का उदाहरण:

अपनी Commandकक्षा के ठोस बोध पर विचार करें - जैसे कुछ CreateUserCommand। अब जाहिर है, जब आप एक नए उपयोगकर्ता को बनाने का अनुरोध करते हैं, तो कमांड को उस उपयोगकर्ता के लिए एक नाम की आवश्यकता होगी। यह देखते हुए कि मुझे CreateUserCommandऔर DIParametersकक्षाओं को पता है, मुझे कौन सा पैरामीटर सेट करना चाहिए?

मैं userNameपैरामीटर सेट कर सकता हूं , या username.. क्या आप पैरामीटर केस को असंवेदनशील तरीके से मानते हैं? मैं सच में नहीं जानता .. ओह रुको .. शायद यह सिर्फ है name?

जैसा कि आप देख सकते हैं कि आप एक सामान्य कुंजी-मूल्य मैपिंग से प्राप्त होने वाली स्वतंत्रता का अर्थ है कि अपनी कक्षाओं का उपयोग करना, जो किसी ने उन्हें लागू नहीं किया है, अनुचित रूप से कठिन है। आपको कम से कम अपने कमांड के लिए कुछ स्थिरांक प्रदान करने की आवश्यकता होगी ताकि दूसरों को यह पता चल सके कि कौन सी कमांड उस कमांड द्वारा समर्थित है।

संभव विभिन्न डिजाइन दृष्टिकोण:

  • अपरिवर्तनीय पैरामीटर: अपने Parameterउदाहरणों को अपरिवर्तनीय रूप से बदलकर आप उन्हें विभिन्न आदेशों के बीच स्वतंत्र रूप से पुन: उपयोग कर सकते हैं।
  • विशिष्ट पैरामीटर कक्षाएं: एक ऐसे UserParameterवर्ग को देखते हुए जिसमें बिल्कुल ऐसे पैरामीटर होते हैं जिनकी मुझे कमांड में एक उपयोगकर्ता की आवश्यकता होती है, इस API के साथ काम करना बहुत सरल होगा। आप अभी भी मापदंडों पर विरासत में हो सकते हैं, लेकिन कमांड वर्गों के लिए किसी भी तरह से मनमाना पैरामीटर लेने के लिए इसका कोई मतलब नहीं होगा - निश्चित रूप से इसका मतलब यह है कि एपीआई उपयोगकर्ताओं को पता है कि कौन से मापदंडों की आवश्यकता है।
  • प्रति संदर्भ एक आदेश उदाहरण: यदि आपको अपने आदेशों जैसी चीजों की आवश्यकता है visitBeforeऔर visitAfter, विभिन्न मापदंडों के साथ उन्हें पुन: उपयोग करते हुए, आप अलग-अलग मापदंडों के साथ कॉल करने की समस्या के लिए खुले रहेंगे। यदि पैरामीटर मल्टीपल मेथड कॉल पर समान होना चाहिए, तो आपको उन्हें इस तरह से कमांड में इनकैप्सुलेट करना होगा, ताकि वे कॉल के बीच-बीच में अन्य मापदंडों के लिए स्विच आउट न कर सकें।

हाँ, मुझे मुलाक़ात से छुटकारा मिल गया। मैं मूल रूप से प्रदर्शन विधि में अपना Diparameter इंटरफ़ेस पास कर रहा हूं। अवांछित DIParamters उदाहरणों के साथ समस्या हमेशा बनी रहने वाली है, क्योंकि मैंने इंटरफ़ेस को पारित करने का लचीलापन चुना है। मैं वास्तव में उपवर्ग को सक्षम करने और DIParameters बच्चों को एक बार भरे जाने के बाद अपरिवर्तनीय बनाने के विचार को पसंद करता हूं। हालांकि, एक "केंद्रीय प्राधिकरण" को अभी भी कमांड के लिए सही Diparameter पास करना होगा। शायद यही कारण है कि मैंने एक विज़िटर पैटर्न को लागू करना शुरू कर दिया
है..मैं

0

डिजाइन सिद्धांतों के बारे में जो अच्छा है वह यह है कि जितनी जल्दी या बाद में, वे एक-दूसरे के साथ संघर्ष करते हैं।

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

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


1
आपको यहाँ कौन से सिद्धांत परस्पर विरोधी लगते हैं?
जिमी हॉफ

बस स्पष्ट करने के लिए, मेरी समस्या प्रसंग या विज़िटर पैटर्न के बीच चयन नहीं है। मैं मूल रूप से एक प्रसंग पैटर्न का उपयोग कर रहा हूं जिसे विज़िटर कहा जाता है :)
एंड्रिया रिचियार्डी

ठीक है, मैं शायद आपके सटीक प्रश्न / समस्या को गलत समझ रहा हूं।
मार्टिन

0

मान लें कि आपके पास एक कमांड इंटरफ़ेस है:

class Command {
public:
    void execute() = 0;
};

और एक विषय:

class Subject {
    std::string name;
public:
    void setName(const std::string& name) {this->name = name;}
}

आपको क्या चाहिए:

class NameObserver {
public:
    void update(const std::string& name) = 0;
};

class Subject {
    NameObserver& o;
    std::string name;
private:
    void setName(const std::string& name) {
        this->name = name;
        o.update(name);
    }
};

class CommandB: public Command, public NameObserver {
    std::string name;
public:
    void execute();
    void update(const std::string& name) {
        this->name = name;
        execute();
    }
};

NameObserver& oकमांडबी के संदर्भ के रूप में सेट करें । अब जब भी CommandA Subjects नाम को बदलता है CommandB सही जानकारी के साथ निष्पादित कर सकता है। यदि नाम का उपयोग अधिक कमांड द्वारा किया जाता है तो astd::list<NameObserver>


जवाब के लिए धन्यवाद। इस डिज़ाइन imho के साथ समस्या यह है कि हमें प्रत्येक पैरामीटर के लिए एक सेटर + NameObserver की आवश्यकता है। मैं एक Diparameters (संदर्भ) उदाहरण को पारित कर सकता हूं और सूचित कर सकता हूं, लेकिन फिर से, मैं शायद इस तथ्य को हल नहीं करूंगा कि मैं अभी भी CommandB को CommandB के साथ युग्मित कर रहा हूं, जिसका अर्थ है कि CommandA को एक महत्वपूर्ण-मूल्य रखना होगा जो केवल CommandB को पता होना चाहिए ... मैंने जो कोशिश की वह भी एक बाहरी संस्था (ParameterHandler) के पास थी जो यह जानने के लिए कि कमांड को कौन से पैरामीटर की जरूरत है और जो Diparameters उदाहरण के अनुसार मिलता है।
एंड्रिया रिचियार्डी

@ काप "इस डिज़ाइन के साथ समस्या यह है कि हमें प्रत्येक पैरामीटर के लिए एक सेटर + NameObserver की आवश्यकता है" - इस संदर्भ में पैरामीटर मेरे लिए थोड़ा भ्रामक है, मैं सोच रहा हूं कि आप क्षेत्र का मतलब है। जिस स्थिति में आपको परिवर्तन करने वाले प्रत्येक क्षेत्र के लिए पहले से ही एक सेटर होना चाहिए। आपके उदाहरण से ऐसा लगता है कि ComamndA Subject का नाम बदल देता है। यह एक सेटर के माध्यम से क्षेत्र को बदलना चाहिए। नोट: आपको प्रति फ़ील्ड पर्यवेक्षक की आवश्यकता नहीं है, बस एक गेटवे है और सभी पर्यवेक्षकों को ऑब्जेक्ट पास करें।
अहिन्दरसन

0

मुझे नहीं पता कि यह प्रोग्रामर्स (जिस स्थिति में मैं माफी मांगता हूं) पर इसे संभालने का सही तरीका है, लेकिन, यहां सभी उत्तरों की जांच करने के बाद (@ फ्रैंक विशेष रूप से)। मैंने अपना कोड इस तरह से वापस ले लिया:

  • गिरा हुआ Diparameters। मैं व्यक्तिगत (सामान्य) वस्तुओं को DIOperation के इनपुट (अपरिवर्तनीय) के रूप में रखूँगा। उदाहरण:
वर्ग संबंधित
निजी:
    std :: string const m_sPrimaryObjectId;
    std :: string const m_sSecondaryObjectId;
    std :: string const m_sRelationObjectId;

    संबंधितऑब्जेक्टट्रिप्ट और ऑपरेटर = (संबंधित ऑबजेक्टट्रिप्ट अन्य);

जनता:
    संबंधितऑब्जेक्टट्रिप्ट (std :: string const & sPrimaryObjectId)
                         std :: string const & sSecondaryObjectId,
                         std :: string const & sRelationObjectId);

    संबंधितऑबजेक्टट्रिप्ट (संबंधित ऑबजेक्टट्रिप कॉन्स्टेंट और अन्य);


    std :: string const & getPrimaryObjectId () कास्ट;
    std :: string const & getSecondaryObjectId () कास्ट;
    std :: string const & getRelationObjectId () कास्ट;

    ~ RelatedObjectTriplet ();
};
  • न्यू डाइपरेशन क्लास (उदाहरण के साथ) के रूप में परिभाषित किया गया है:
टेम्पलेट <वर्ग टी = शून्य> 
क्लास डिओपरेशन {
जनता:
    आभासी इंट प्रदर्शन () = 0;

    वर्चुअल टी getResult () = 0;

    वर्चुअल ~ डाइपरेशन () = 0;
};

क्लास क्रिएशन: पब्लिक डिओपरेशन <संबंधितऑब्जेक्टट्रिप्टल> {
निजी:
    स्थिर std :: string const TYPE;

    // परम (अपरिवर्तनीय)
    संबंधितऑब्जेक्टट्रिपल कॉन्स्ट m_sParams;

    // छिपा हुआ
    CreateRelation & oper = (CreateRelation const & source);
    CreateRelation (CreateRelation const & source);

    // अंदर का
    std :: string m_sNewRelationId;

जनता:
    CreateRelation (RelatedObjectTriplet const & params);

    int प्रदर्शन ();

    संबंधितObjectTriplet getResult ();

    ~ CreateRelation ();
};
  • इसका उपयोग इस तरह किया जा सकता है:
RelatedObjectTriplet triplet ("33333", "55555", "77777");
CreateRelation createRel (triplet);
createRel.perform ();
const RelatedObjectTriplet Res = createRel.getResult ();

मदद के लिए धन्यवाद और मुझे आशा है कि मैंने यहाँ गलतियाँ नहीं की हैं :)

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