CodeGym /Java Blog /अनियमित /रणनीति डिजाइन पैटर्न
John Squirrels
स्तर 41
San Francisco

रणनीति डिजाइन पैटर्न

अनियमित ग्रुप में प्रकाशित
नमस्ते! आज के पाठ में हम रणनीति पैटर्न के बारे में बात करेंगे। पिछले पाठों में, हम विरासत की अवधारणा से संक्षिप्त रूप से परिचित हो चुके हैं। यदि आप भूल गए हैं, तो मैं आपको याद दिला दूं कि यह शब्द एक सामान्य प्रोग्रामिंग कार्य के मानक समाधान को संदर्भित करता है। CodeGym में, हम अक्सर कहते हैं कि आप लगभग किसी भी प्रश्न का उत्तर गूगल कर सकते हैं। ऐसा इसलिए है क्योंकि आपका कार्य, चाहे वह कुछ भी हो, शायद पहले ही किसी और के द्वारा सफलतापूर्वक हल कर लिया गया है। पैटर्न सबसे सामान्य कार्यों, या समस्याग्रस्त स्थितियों को हल करने के तरीकों के लिए आजमाए गए और सच्चे समाधान हैं। ये "पहिए" की तरह हैं जिन्हें आपको अपने दम पर फिर से आविष्कार करने की आवश्यकता नहीं है, लेकिन आपको यह जानने की आवश्यकता है कि उन्हें कैसे और कब उपयोग करना है :) पैटर्न का एक अन्य उद्देश्य समान वास्तुकला को बढ़ावा देना है। किसी और का कोड पढ़ना कोई आसान काम नहीं है! हर कोई अलग कोड लिखता है, क्योंकि एक ही काम को कई तरह से हल किया जा सकता है। लेकिन पैटर्न का उपयोग विभिन्न प्रोग्रामर को कोड की प्रत्येक पंक्ति में जाने के बिना प्रोग्रामिंग लॉजिक को समझने में मदद करता है (यहां तक ​​​​कि इसे पहली बार देखने पर भी!) आज हम "रणनीति" नामक सबसे सामान्य डिज़ाइन पैटर्न में से एक को देखते हैं। डिजाइन पैटर्न: रणनीति - 2कल्पना कीजिए कि हम एक प्रोग्राम लिख रहे हैं जो सक्रिय रूप से कन्वेयन्स ऑब्जेक्ट्स के साथ काम करेगा। इससे वास्तव में कोई फर्क नहीं पड़ता कि वास्तव में हमारा कार्यक्रम क्या करता है। हमने एक कन्वेयन्स पेरेंट क्लास और तीन चाइल्ड क्लासेस के साथ एक क्लास पदानुक्रम बनाया है: सेडान , ट्रक और F1Car

public class Conveyance {

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {

       System.out.println("Braking!");
   }
}

public class Sedan extends Conveyance {
}

public class Truck extends Conveyance {
}

public class F1Car extends Conveyance {
}
सभी तीन बाल वर्ग माता-पिता से दो मानक विधियाँ प्राप्त करते हैं: गो () और स्टॉप () । हमारा कार्यक्रम बहुत सरल है: हमारी कारें केवल आगे बढ़ सकती हैं और ब्रेक लगा सकती हैं। अपना काम जारी रखते हुए, हमने कारों को एक नया तरीका देने का फैसला किया: फिल () (अर्थात्, "गैस टैंक भरें")। हमने इसे कन्वेन्स पैरेंट क्लास में जोड़ा :

public class Conveyance {

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {

       System.out.println("Braking!");
   }
  
   public void fill() {
       System.out.println("Refueling!");
   }
}
क्या इतनी साधारण स्थिति में वास्तव में समस्याएँ उत्पन्न हो सकती हैं? दरअसल, उनके पास पहले से ही... डिजाइन पैटर्न: रणनीति - 3

public class Stroller extends Conveyance {

   public void fill() {
      
       // Hmm... This is a stroller for children. It doesn't need to be refueled :/
   }
}
हमारे कार्यक्रम में अब एक वाहन (एक बच्चा घुमक्कड़) है जो सामान्य अवधारणा में अच्छी तरह से फिट नहीं होता है। इसमें पैडल हो सकते हैं या रेडियो-नियंत्रित हो सकते हैं, लेकिन एक बात निश्चित है - इसमें गैस डालने के लिए कोई जगह नहीं होगी। हमारे वर्ग पदानुक्रम ने सामान्य विधियों को उन वर्गों द्वारा विरासत में प्राप्त करने का कारण बना दिया है जिनकी उन्हें आवश्यकता नहीं है। इस स्थिति में हमें क्या करना चाहिए? ठीक है, हम घुमक्कड़ वर्ग में भरण () विधि को ओवरराइड कर सकते हैं ताकि जब आप घुमक्कड़ को ईंधन भरने की कोशिश करें तो कुछ न हो:

public class Stroller extends Conveyance {

   @Override
   public void fill() {
       System.out.println("A stroller cannot be refueled!");
   }
}
लेकिन डुप्लिकेट कोड के अलावा किसी अन्य कारण से इसे शायद ही सफल समाधान कहा जा सकता है। उदाहरण के लिए, अधिकांश वर्ग मूल वर्ग की विधि का उपयोग करेंगे, लेकिन बाकी को इसे ओवरराइड करने के लिए मजबूर किया जाएगा। यदि हमारे पास 15 वर्ग हैं और हमें उनमें से 5-6 में व्यवहार को ओवरराइड करना चाहिए, तो कोड दोहराव काफी व्यापक हो जाएगा। शायद इंटरफेस हमारी मदद कर सकते हैं? उदाहरण के लिए, इस तरह:

public interface Fillable {
  
   public void fill();
}
हम एक भरण () विधि के साथ एक भरण योग्य इंटरफ़ेस बनाएंगे । फिर, वे वाहन जिन्हें ईंधन भरने की आवश्यकता है, वे इस इंटरफ़ेस को लागू करेंगे, जबकि अन्य वाहन (उदाहरण के लिए, हमारा बेबी घुमक्कड़) नहीं करेंगे। लेकिन यह विकल्प हमें शोभा नहीं देता। भविष्य में, हमारा वर्ग पदानुक्रम बहुत बड़ा हो सकता है (जरा सोचिए कि दुनिया में कितने अलग-अलग प्रकार के वाहन हैं)। हमने वंशानुक्रम से जुड़े पिछले संस्करण को छोड़ दिया, क्योंकि हम भरण को ओवरराइड नहीं करना चाहते हैं ()विधि कई, कई बार। अब हमें इसे हर वर्ग में लागू करना है ! और अगर हमारे पास 50 हैं तो क्या होगा? और अगर हमारे कार्यक्रम में बार-बार बदलाव किए जाते हैं (और यह वास्तविक कार्यक्रमों के लिए लगभग हमेशा सच होता है!), तो हमें सभी 50 वर्गों में भाग लेना होगा और उनमें से प्रत्येक के व्यवहार को मैन्युअल रूप से बदलना होगा। तो आखिर में हमें इस स्थिति में क्या करना चाहिए? अपनी समस्या को हल करने के लिए, हम एक अलग तरीका चुनेंगे। अर्थात्, हम अपने वर्ग के व्यवहार को कक्षा से ही अलग कर देंगे। इसका क्या मतलब है? जैसा कि आप जानते हैं, प्रत्येक वस्तु में स्थिति (डेटा का एक सेट) और व्यवहार (तरीकों का एक सेट) होता है। हमारे परिवहन वर्ग के व्यवहार में तीन विधियाँ शामिल हैं: go() , stop() और fill() । पहले दो तरीके जैसे हैं वैसे ही ठीक हैं। लेकिन हम तीसरी विधि को इसमें से बाहर करेंगेसंवहन वर्ग। यह व्यवहार को कक्षा से अलग कर देगा (अधिक सटीक रूप से, यह व्यवहार का केवल एक हिस्सा अलग करेगा, क्योंकि पहले दो तरीके वहीं रहेंगे जहाँ वे हैं)। तो हमें अपनी भरण () विधि कहाँ रखनी चाहिए ? दिमाग में कुछ नहीं आता:/ऐसा लगता है कि यह वही है जहां इसे होना चाहिए। हम इसे एक अलग इंटरफ़ेस में ले जाएँगे: FillStrategy !

public interface FillStrategy {

   public void fill();
}
हमें ऐसे इंटरफ़ेस की आवश्यकता क्यों है? यह सब सीधा है। अब हम इस इंटरफेस को लागू करने वाले कई वर्ग बना सकते हैं:

public class HybridFillStrategy implements FillStrategy {
  
   @Override
   public void fill() {
       System.out.println("Refuel with gas or electricity — your choice!");
   }
}

public class F1PitstopStrategy implements FillStrategy {
  
   @Override
   public void fill() {
       System.out.println("Refuel with gas only after all other pit stop procedures are complete!");
   }
}

public class StandardFillStrategy implements FillStrategy {
   @Override
   public void fill() {
       System.out.println("Just refuel with gas!");
   }
}
हमने तीन व्यवहार रणनीतियाँ बनाईं: एक सामान्य कारों के लिए, एक हाइब्रिड के लिए, और एक फॉर्मूला 1 रेस कारों के लिए। प्रत्येक रणनीति एक अलग ईंधन भरने वाले एल्गोरिदम को लागू करती है। हमारे मामले में, हम केवल कंसोल पर एक स्ट्रिंग प्रदर्शित करते हैं, लेकिन प्रत्येक विधि में कुछ जटिल तर्क शामिल हो सकते हैं। अब इसके बाद हम क्या करें?

public class Conveyance {

   FillStrategy fillStrategy;

   public void fill() {
       fillStrategy.fill();
   }

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {
       System.out.println("Braking!");
   }
  
}
हम अपने FillStrategy इंटरफ़ेस का उपयोग कन्वेन्स पैरेंट क्लास में एक फ़ील्ड के रूप में करते हैं। ध्यान दें कि हम एक विशिष्ट कार्यान्वयन का संकेत नहीं दे रहे हैं - हम एक इंटरफ़ेस का उपयोग कर रहे हैं। कार क्लासेस को FillStrategy इंटरफ़ेस के विशिष्ट कार्यान्वयन की आवश्यकता होगी:

public class F1Car extends Conveyance {

   public F1Car() {
       this.fillStrategy = new F1PitstopStrategy();
   }
}

public class HybridCar extends Conveyance {

   public HybridCar() {
       this.fillStrategy = new HybridFillStrategy();
   }
}

public class Sedan extends Conveyance {

   public Sedan() {
       this.fillStrategy = new StandardFillStrategy();
   }
}

आइए देखें कि हमें क्या मिला!

public class Main {

   public static void main(String[] args) {

       Conveyance sedan = new Sedan();
       Conveyance hybrid = new HybridCar();
       Conveyance f1car = new F1Car();

       sedan.fill();
       hybrid.fill();
       f1car.fill();
   }
}
कंसोल आउटपुट:

Just refuel with gas! 
Refuel with gas or electricity — your choice! 
Refuel with gas only after all other pit stop procedures are complete!
महान! ईंधन भरने की प्रक्रिया उसी तरह काम करती है जैसा उसे करना चाहिए! वैसे, कंस्ट्रक्टर में एक पैरामीटर के रूप में रणनीति का उपयोग करने से हमें कुछ भी नहीं रोकता है! उदाहरण के लिए, इस तरह:

public class Conveyance {

   private FillStrategy fillStrategy;

   public Conveyance(FillStrategy fillStrategy) {
       this.fillStrategy = fillStrategy;
   }

   public void fill() {
       this.fillStrategy.fill();
   }

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {
       System.out.println("Braking!");
   }
}

public class Sedan extends Conveyance {

   public Sedan() {
       super(new StandardFillStrategy());
   }
}



public class HybridCar extends Conveyance {

   public HybridCar() {
       super(new HybridFillStrategy());
   }
}

public class F1Car extends Conveyance {

   public F1Car() {
       super(new F1PitstopStrategy());
   }
}
चलिए अपना main() मेथड चलाते हैं (जो अपरिवर्तित रहता है)। हमें वही परिणाम मिलता है! कंसोल आउटपुट:

Just refuel with gas! 
Refuel with gas or electricity — your choice! 
Refuel with gas only after all other pit stop procedures are complete!
रणनीति डिजाइन पैटर्न एल्गोरिदम के एक परिवार को परिभाषित करता है, उनमें से प्रत्येक को समाहित करता है, और यह सुनिश्चित करता है कि वे विनिमेय हैं। यह आपको क्लाइंट द्वारा उपयोग किए जाने के बावजूद एल्गोरिदम को संशोधित करने देता है (यह परिभाषा, "हेड फर्स्ट डिज़ाइन पैटर्न" पुस्तक से ली गई है, मुझे उत्कृष्ट लगती है)। डिजाइन पैटर्न: रणनीति - 4हमने पहले से ही अलग-अलग कार्यान्वयन के साथ अलग-अलग इंटरफेस में एल्गोरिदम के परिवार को निर्दिष्ट किया है जिसमें हम रुचि रखते हैं (कारों को ईंधन भरने के तरीके)। हमने उन्हें कार से ही अलग कर दिया। अब अगर हमें किसी विशेष ईंधन भरने वाले एल्गोरिथ्म में कोई बदलाव करने की आवश्यकता है, तो यह किसी भी तरह से हमारी कार की कक्षाओं को प्रभावित नहीं करेगा। और विनिमेयता को प्राप्त करने के लिए, हमें अपने कन्वेन्स क्लास में केवल एक सेटर विधि जोड़ने की आवश्यकता है :

public class Conveyance {

   FillStrategy fillStrategy;

   public void fill() {
       fillStrategy.fill();
   }

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {
       System.out.println("Braking!");
   }

   public void setFillStrategy(FillStrategy fillStrategy) {
       this.fillStrategy = fillStrategy;
   }
}
अब हम चलते-फिरते रणनीतियां बदल सकते हैं:

public class Main {

   public static void main(String[] args) {

       Stroller stroller= new Stroller();
       stroller.setFillStrategy(new StandardFillStrategy());

       stroller.fill();
   }
}
यदि बेबी स्ट्रॉलर अचानक गैसोलीन पर चलने लगे, तो हमारा कार्यक्रम इस परिदृश्य को संभालने के लिए तैयार हो जाएगा :) और बस इतना ही! आपने एक और डिज़ाइन पैटर्न सीखा है जो निस्संदेह वास्तविक परियोजनाओं पर काम करते समय आवश्यक और सहायक होगा :) अगली बार तक!
टिप्पणियां
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION