एक प्रतिक्रिया को संभालने के लिए डिजाइन पैटर्न


10

अधिकांश समय जब मैं कुछ कोड लिख रहा होता हूं जो एक निश्चित फ़ंक्शन कॉल के लिए प्रतिक्रिया को हैंडल करता है मुझे निम्नलिखित कोड संरचना मिलती है:

उदाहरण: यह एक फ़ंक्शन है जो एक लॉगिन सिस्टम के लिए प्रमाणीकरण को संभाल लेगा

class Authentication{

function login(){ //This function is called from my Controller
$result=$this->authenticate($username,$password);

if($result=='wrong password'){
   //increase the login trials counter
   //send mail to admin
   //store visitor ip    
}else if($result=='wrong username'){
   //increase the login trials counter
   //do other stuff
}else if($result=='login trials exceeded')
   //do some stuff
}else if($result=='banned ip'){
   //do some stuff
}else if...

function authenticate($username,$password){
   //authenticate the user locally or remotely and return an error code in case a login in fails.
}    
}

मुसीबत

  1. जैसा कि आप देख सकते हैं कि कोड एक if/elseसंरचना पर बना हुआ है जिसका मतलब है कि एक नई विफलता की स्थिति का मतलब होगा कि मुझे एक else ifबयान जोड़ने की आवश्यकता है जो ओपन बंद सिद्धांत के लिए उल्लंघन है ।
  2. मुझे लगता है कि फ़ंक्शन में अमूर्तता की अलग-अलग परतें हैं क्योंकि मैं सिर्फ एक हैंडलर में लॉगिन परीक्षण काउंटर बढ़ा सकता हूं, लेकिन दूसरे में अधिक गंभीर सामान कर सकता हूं।
  3. कुछ कार्यों को increase the login trialsउदाहरण के लिए दोहराया जाता है।

मैंने एकाधिक if/elseको फ़ैक्टरी पैटर्न में परिवर्तित करने के बारे में सोचा , लेकिन मैंने केवल वस्तुओं को बदलने के लिए फ़ैक्टरी का उपयोग किया, जो व्यवहार में परिवर्तन न करें। क्या किसी के पास इसके लिए कोई बेहतर उपाय है?

ध्यान दें:

यह एक लॉगिन सिस्टम का उपयोग करने के लिए सिर्फ एक उदाहरण है। मैं एक अच्छी तरह से निर्मित ओओ पैटर्न का उपयोग करके इस व्यवहार के लिए एक सामान्य समाधान के लिए पूछ रहा हूं। इस तरह के if/elseहैंडलर मेरे कोड में बहुत सी जगहों पर दिखाई देते हैं और मैंने उदाहरण के लिए एक सरल आसान के रूप में लॉगिन सिस्टम का उपयोग किया। मेरे वास्तविक उपयोग के मामले यहां पोस्ट करने के लिए बहुत जटिल हैं। : डी

कृपया अपने उत्तर को PHP कोड तक सीमित न रखें और अपनी पसंद की भाषा का उपयोग करने के लिए स्वतंत्र महसूस करें।


अपडेट करें

मेरे प्रश्न को स्पष्ट करने के लिए एक और अधिक जटिल कोड उदाहरण:

  public function refundAcceptedDisputes() {            
        $this->getRequestedEbayOrdersFromDB(); //get all disputes requested on ebay
        foreach ($this->orders as $order) { /* $order is a Doctrine Entity */
            try {
                if ($this->isDisputeAccepted($order)) { //returns true if dispute was accepted
                    $order->setStatus('accepted');
                    $order->refund(); //refunds the order on ebay and internally in my system
                    $this->insertRecordInOrderHistoryTable($order,'refunded');                        
                } else if ($this->isDisputeCancelled($order)) { //returns true if dispute was cancelled
                    $order->setStatus('cancelled');
                    $this->insertRecordInOrderHistory($order,'cancelled');
                    $order->rollBackRefund(); //cancels the refund on ebay and internally in my system
                } else if ($this->isDisputeOlderThan7Days($order)) { //returns true if 7 days elapsed since the dispute was opened
                    $order->closeDispute(); //closes the dispute on ebay
                    $this->insertRecordInOrderHistoryTable($order,'refunded');
                    $order->refund(); //refunds the order on ebay and internally in my system
                }
            } catch (Exception $e) {
                $order->setStatus('failed');
                $order->setErrorMessage($e->getMessage());
                $this->addLog();//log error
            }
            $order->setUpdatedAt(time());
            $order->save();
        }
    }

समारोह का उद्देश्य:

  • मैं ईबे पर गेम बेच रहा हूं।
  • अगर कोई ग्राहक अपने ऑर्डर को रद्द करना चाहता है और अपने पैसे वापस करता है (यानी रिफंड) तो मुझे पहले eBay पर "विवाद" खोलना होगा।
  • एक बार विवाद खुलने के बाद, मुझे ग्राहक की पुष्टि करने के लिए प्रतीक्षा करनी चाहिए कि वह धनवापसी के लिए सहमत हो (मूर्खतापूर्ण रूप से वह वही है जिसने मुझे धनवापसी करने के लिए कहा था, लेकिन यह कैसे eBay पर काम करता है)।
  • यह कार्य मेरे द्वारा खोले गए सभी विवादों को प्राप्त करता है और समय-समय पर उनकी स्थिति की जांच करता है कि ग्राहक ने विवाद का जवाब दिया है या नहीं।
  • ग्राहक सहमत हो सकता है (तब मैं धनवापसी करता हूं) या फिर मना कर देता हूं (तब मैं रोलबैक करता हूं) या 7 दिनों तक जवाब नहीं दे सकता (मैं खुद को फिर विवाद वापस करता हूं)।

जवाबों:


15

यह रणनीति पैटर्न के लिए एक प्रमुख उम्मीदवार है ।

उदाहरण के लिए, यह कोड:

if ($this->isDisputeAccepted($order)) { //returns true if dispute was accepted
    $order->setStatus('accepted');
    $order->refund(); //refunds the order on ebay and internally in my system
    $this->insertRecordInOrderHistoryTable($order,'refunded');                        
} else if ($this->isDisputeCancelled($order)) { //returns true if dispute was cancelled
    $order->setStatus('cancelled');
    $this->insertRecordInOrderHistory($order,'cancelled');
    $order->rollBackRefund(); //cancels the refund on ebay and internally in my system
} else if ($this->isDisputeOlderThan7Days($order)) { //returns true if 7 days elapsed since the dispute was opened
    $order->closeDispute(); //closes the dispute on ebay
    $this->insertRecordInOrderHistoryTable($order,'refunded');
    $order->refund(); //refunds the order on ebay and internally in my system
}

को कम किया जा सकता है

var $strategy = $this.getOrderStrategy($order);
$strategy->preProcess();
$strategy->updateOrderHistory($this);
$strategy->postProcess();

जहाँ getOrderStrategy ऑर्डर को DisputeAcceptedStrategy, DisputeCancelledStrategy, DisputeOlderThan7DaysStrategy आदि में लपेटता है, जिनमें से प्रत्येक को पता है कि दिए गए स्थिति को कैसे संभालना है।

टिप्पणियों में प्रश्न का उत्तर देने के लिए संपादित करें।

क्या आप कृपया अपने कोड पर अधिक जानकारी दे सकते हैं। मैंने जो समझा वह यह है कि getOrderStrategy एक फैक्ट्री मेथड है जो ऑर्डर की स्थिति के आधार पर एक रणनीति ऑब्जेक्ट देता है, लेकिन प्रीप्रोसेस () और प्रीप्रोसेस () फ़ंक्शन क्या हैं। इसके अलावा आपने अपडेट के लिए यह $ पास क्यों किया हैऑडरहिस्टॉर (यह $)?

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

आपके पास जो एक आम कोड है, वह सम्मिलित हैइन्कॉर्डऑर्डरऑडरहिस्टॉरटेबल, इसलिए मैंने रणनीति के केंद्रीय बिंदु के रूप में (थोड़ा और सामान्य नाम के साथ) उसका उपयोग करना चुना। मैं इसे $ पास करता हूं, क्योंकि यह इस पर एक तरीका कह रहा है, $ ऑर्डर और प्रति रणनीति एक अलग स्ट्रिंग के साथ।

इसलिए, मूल रूप से, मैं इस तरह से देखने वालों में से प्रत्येक की कल्पना करता हूं:

public function updateOrderHistory($auth) {
    $auth.insertRecordInOrderHistoryTable($order, 'cancelled');
}

जहां $ आदेश रणनीति का एक निजी सदस्य है (याद रखें मैंने कहा था कि इसे आदेश को लपेटना चाहिए) और दूसरा तर्क प्रत्येक वर्ग में अलग है। फिर, यह पूरी तरह से अनुचित हो सकता है। आप InsertRecordInOrderHistoryTable को एक बेस स्ट्रेटेजी क्लास में ले जाना चाहते हैं और ऑथराइजेशन क्लास को पास नहीं करना चाहते। या आप कुछ अलग करना चाहते हैं, यह सिर्फ एक उदाहरण था।

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

आप ऐसा करना पसंद कर सकते हैं:

var $strategy = $this.getOrderStrategy($order);
$strategy->setStatus();
$strategy->closeDisputeIfNecessary();
$strategy->refundIfNecessary();
$strategy->insertRecordInOrderHistoryTable($this);                        
$strategy->rollBackRefundIfNecessary();

और आपकी कुछ रणनीतियाँ "IfN जरूरी" तरीकों के लिए खाली तरीकों को लागू करती हैं।

जो भी कॉलिंग कोड को अधिक पठनीय बनाता है।


आपके उत्तर के लिए धन्यवाद, लेकिन क्या आप कृपया अपने कोड पर अधिक जानकारी दे सकते हैं। मैंने जो समझा वह getOrderStrategyएक फ़ैक्टरी विधि है जो strategyऑर्डर की स्थिति के आधार पर एक वस्तु लौटाती है , लेकिन कार्य preProcess()और preProcess()कार्य क्या हैं । आप भी क्यों पास $thisहुए updateOrderHistory($this)?
सोंगो

1
@Songo: आशा है कि ऊपर संपादित मदद करता है।
पीडीआर

अहा! मुझे लगता है मैं अब समझ गया। निश्चित रूप से मुझ से एक अप-वोट :)
सांगो

+1, कैन, आप विस्तार से बता सकते हैं कि क्या लाइन, var $ रणनीति = $ this.getOrderStrategy ($ ऑर्डर); रणनीति की पहचान करने के लिए एक स्विच केस होगा।
नवीन कुमार

2

रणनीति पैटर्न एक अच्छा सुझाव है यदि आप वास्तव में अपने तर्क को विकेंद्रीकृत करना चाहते हैं, लेकिन ऐसा लगता है कि उदाहरण के लिए अप्रत्यक्ष ओवरकिल आपके जैसे छोटे हैं। व्यक्तिगत रूप से, मैं "छोटे कार्य लिखूंगा" पैटर्न को नियोजित करूंगा, जैसे:

if($result=='wrong password')
   wrongPassword();
else if($result=='wrong username')
   wrongUsername();
else if($result=='login trials exceeded')
   excessiveTries();
else if($result=='banned ip')
   bannedIp();

1

जब आप यदि / तो / किसी और बयान एक स्थिति को संभालने के लिए के एक समूह के होने शुरू करते हैं, पर विचार राज्य पैटर्न

इसका उपयोग करने के एक विशेष तरीके पर एक सवाल था: क्या राज्य पैटर्न के इस कार्यान्वयन का कोई मतलब है?

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


0

जैसा कि मैंने अपनी टिप्पणियों में कहा, जटिल तर्क वास्तव में कुछ भी नहीं बदलता है।

आप एक विवादित आदेश को संसाधित करना चाहते हैं। ऐसा करने के कई तरीके हैं। विवादित आदेश प्रकार हो सकता है Enum:

public void ProcessDisputedOrder(DisputedOrder order)
{
   switch (order.Type)
   {
       case DisputedOrderType.Canceled:
          var strategy = new StrategyForDisputedCanceledOrder();
          strategy.Process(order);  
          break;

       case DisputedOrderType.LessThan7Days:
          var strategy = new DifferentStrategy();
          strategy.Process(order);
          break;

       default: 
          throw new NotImplementedException();
   }
}

ऐसा करने के कई तरीके हैं। आप की वंशानुगत पदानुक्रम हो सकता है Order, DisputedOrder, DisputedOrderLessThan7Days, DisputedOrderCanceled, आदि यह अच्छा नहीं है, लेकिन यह भी काम करेगा।

ऊपर के मेरे उदाहरण में मैं ऑर्डर प्रकार देखता हूं और उसके लिए प्रासंगिक रणनीति प्राप्त करता हूं। आप उस प्रक्रिया को एक कारखाने में संलग्न कर सकते हैं:

var strategy = DisputedOrderStrategyFactory.Instance.Build(order.Type);

यह ऑर्डर के प्रकार को देखता है और आपको उस प्रकार के ऑर्डर के लिए एक सही रणनीति देता है।

आप कुछ की तर्ज पर समाप्त हो सकते हैं:

public void ProcessDisputedOrder(DisputedOrder order)
{
   var strategy = DisputedOrderStrategyFactory.Instance.Build(order.Type);   
   strategy.Process(order);
}

मूल उत्तर, अब प्रासंगिक नहीं जैसा कि मैंने सोचा था कि आप कुछ सरल के बाद थे:

मैं निम्नलिखित चिंताओं को यहां देख रहा हूं:

  • प्रतिबंधित आईपी के लिए जाँच करें। यह जाँच करता है कि उपयोगकर्ता का IP किसी प्रतिबंधित IP श्रेणी में है या नहीं। आप इस पर कोई अमल नहीं करेंगे।
  • जाँच करें कि क्या परीक्षण हुआ। जाँचता है कि क्या उपयोगकर्ता ने अपने लॉगिन प्रयासों को पार कर लिया है। आप इस पर कोई अमल नहीं करेंगे।
  • उपयोगकर्ता को प्रमाणित करें। उपयोगकर्ता को प्रमाणित करने का प्रयास।

मैं निम्नलिखित कार्य करूंगा:

CheckBannedIP(login.IP);
CheckLoginTrial(login);

Authenticate(login.Username, login.Password);

public void CheckBannedIP(string ip)
{
    // If banned then re-direct, else do nothing.
}

public void CheckLoginTrial(LoginAttempt login)
{
    // If exceeded trials, then inform user, else do nothing
}

public void Authenticate(string username, string password)
{
     // Attempt to authenticate. On success redirect, else catch any errors and inform the user. 
}

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

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


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

यह कुछ भी नहीं बदलता है। जिम्मेदारी कुछ रणनीति का उपयोग करके विवादित आदेश को संसाधित करना है। विवाद के प्रकार के साथ रणनीति भिन्न होगी।
कोडर्ट

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

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