क्या महत्वपूर्ण पैटर्न के बिना रणनीति पैटर्न को लागू किया जा सकता है?


14

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

फैक्टरी:

// All of these classes implement OrderStrategy
switch (orderType) {
case NEW_ORDER: return new NewOrder();
case CANCELLATION: return new Cancellation();
case RETURN: return new Return();
}

इसके बाद के कोड को चिंता करने की आवश्यकता नहीं है, और अब नए ऑर्डर प्रकार को जोड़ने के लिए केवल एक ही जगह है, लेकिन कोड का यह खंड अभी भी उपलब्ध नहीं है। इसे डेटा फ़ाइल में खींचने से पठनीयता में कुछ हद तक मदद मिलती है (बहस योग्य, मुझे पता है):

<strategies>
   <order type="NEW_ORDER">com.company.NewOrder</order>
   <order type="CANCELLATION">com.company.Cancellation</order>
   <order type="RETURN">com.company.Return</order>
</strategies>

लेकिन यह अभी भी डेटा फ़ाइल को संसाधित करने के लिए बॉयलरप्लेट कोड जोड़ता है - दी गई, अधिक आसानी से इकाई परीक्षण योग्य और अपेक्षाकृत स्थिर कोड, लेकिन फिर भी अतिरिक्त जटिलता।

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

क्या इस जटिलता को कम करने वाले रणनीति पैटर्न को लागू करने का एक तरीका है? या यह बस के रूप में के रूप में सरल है, और आगे जाने की कोशिश केवल अमूर्त की एक और परत जोड़ने के लिए थोड़ा कोई फायदा नहीं होगा?


हम्म्म्म .... चीजों को सरल करना संभव हो सकता है जैसे eval... जावा में काम नहीं कर सकते हैं, लेकिन शायद अन्य भाषाओं में?
FrustratedWithFormsDesigner

1
@FrustratedWithFormsDesigner प्रतिबिंब जावा में जादू का शब्द है
शाफ़्ट सनकी

2
आपको अभी भी उन स्थितियों की आवश्यकता होगी। उन्हें एक कारखाने में धकेलने से, आप बस DRY का अनुपालन कर रहे हैं, अन्यथा यदि कई स्थानों पर if या switch स्टेटमेंट दिखाई दे सकते हैं।
डेनियल बी

1
जिस दोष का आप उल्लेख कर रहे हैं, उसे अक्सर खुले-बंद-शिष्य का उल्लंघन
k3b

संबंधित प्रश्न में स्वीकृत उत्तर शब्दकोश / मानचित्र को if-else और switch के विकल्प के रूप में बताता है
gnat

जवाबों:


16

बिलकूल नही। यहां तक ​​कि अगर आप एक आईओसी कंटेनर का उपयोग करते हैं, तो आपको कहीं न कहीं यह भी तय करना होगा कि कौन सा ठोस इंजेक्शन लगाना है। यह रणनीति पैटर्न की प्रकृति है।

मैं वास्तव में नहीं देखता कि लोग क्यों सोचते हैं कि यह एक समस्या है। कुछ किताबों में कथन हैं, जैसे फाउलर की रीफैक्टरिंग , कि अगर आपको एक स्विच / केस या अन्य कोड के बीच में / elses की एक श्रृंखला दिखाई देती है, तो आपको विचार करना चाहिए कि एक गंध और इसे अपने तरीके से स्थानांतरित करने के लिए देखें। यदि प्रत्येक मामले में कोड एक पंक्ति से अधिक है, शायद दो, तो आपको उस विधि को फ़ैक्टरी विधि बनाने पर विचार करना चाहिए, जो रणनीतियाँ लौटाती हैं।

कुछ लोगों ने इसका मतलब यह निकाला है कि एक स्विच / केस खराब है। यह मामला नहीं है। लेकिन यदि संभव हो तो इसे अपने आप ही रहना चाहिए।


1
मैं इसे बुरी चीज के रूप में नहीं देखता। हालांकि, मैं हमेशा कोड की मात्रा को कम करने के तरीकों की तलाश कर रहा हूं, जो इसे बनाए रखेंगे, जिससे सवाल पैदा हो।
माइकल के

स्विच स्टेटमेंट (और लंबे समय तक / और-अगर-ब्लॉक) खराब हैं और आपके कोड को बनाए रखने के लिए यदि संभव हो तो इसे टाला जाना चाहिए। उन्होंने कहा कि "यदि संभव हो तो" यह स्वीकार करता है कि कुछ ऐसे मामले हैं जहां स्विच मौजूद होना चाहिए, और उन मामलों में, इसे एक ही स्थान पर रखने का प्रयास करें, और एक ऐसा जो इसे कम से कम बनाए रखता है (यह इतना आसान है) गलती से कोड में 5 स्थानों में से 1 याद आती है जिसे आपको सिंक में रखने की आवश्यकता होती है जब आप इसे ठीक से अलग नहीं करते हैं)।
शैडो मैन

10

क्या महत्वपूर्ण पैटर्न के बिना रणनीति पैटर्न को लागू किया जा सकता है?

हां , एक हैशमैप / डिक्शनरी का उपयोग करके जहां हर रणनीति कार्यान्वयन रजिस्टर करता है। कारखाना विधि कुछ इस तरह हो जाएगी

Class strategyType = allStrategies[orderType];
return runtime.create(strategyType);

प्रत्येक रणनीति कार्यान्वयन को अपने ऑर्डरटाइप के साथ कारखाने को पंजीकृत करना होगा और कुछ जानकारी कैसे एक वर्ग बनाना है।

factory.register(NEW_ORDER, NewOrder.class);

आप पंजीकरण के लिए स्थैतिक निर्माता का उपयोग कर सकते हैं, अगर आपकी भाषा इस का समर्थन करती है।

रजिस्टर विधि हैशमैप में एक नया मान जोड़ने से ज्यादा कुछ नहीं करती है:

void register(OrderType orderType, Class class)
{
   allStrategies[orderType] = class;
}

[अद्यतन 2012-05-04]
यह समाधान मूल "स्विच समाधान" की तुलना में बहुत अधिक जटिल है जो मैं ज्यादातर समय पसंद करूंगा।

हालाँकि, ऐसे वातावरण में जहाँ रणनीतियाँ अक्सर बदलती रहती हैं (यानी ग्राहक, समय, .... के आधार पर मूल्य गणना) आईओसी-कंटेनर के साथ संयुक्त हैशमैप समाधान अच्छा समाधान हो सकता है।


3
तो अब आपको स्विच / केस से बचने के लिए, स्ट्रेटेजीज़ का एक पूरा हैशमैप मिल गया है? पंक्तियों की तुलना में वास्तव में factory.register(NEW_ORDER, NewOrder.class);क्लीनर की पंक्तियाँ या OCP का उल्लंघन कितना कम है case NEW_ORDER: return new NewOrder();?
पीडीआर

5
यह बिल्कुल साफ नहीं है। यह प्रति-सहज है। यह खुले-बंद-priciple को बेहतर बनाने के लिए रखने-के-उत्तेजना-बेवकूफ-सिद्धांत का त्याग करता है। वही उलटा-के-नियंत्रण और निर्भरता-इंजेक्शन पर लागू होता है: ये एक सरल समाधान की तुलना में समझना अधिक कठिन हैं। सवाल "लागू किया गया था ... महत्वपूर्ण शाखाओं में बंटी बिना" और "कैसे एक अधिक सहज समाधान बनाने के लिए"
k3b

1
मैं नहीं देखता कि आपने ब्रांचिंग से परहेज किया है। आपने सिंटैक्स को बदल दिया है।
पीडीआर

1
क्या भाषाओं में इस समाधान के साथ कोई समस्या नहीं है जो तब तक कक्षाओं को लोड नहीं करते हैं जब तक उन्हें ज़रूरत नहीं है? स्थिर कोड तब तक नहीं चलेगा जब तक कि क्लास को लोड नहीं किया जाता है, और क्लास को कभी भी लोड नहीं किया जाएगा क्योंकि कोई अन्य वर्ग इसे संदर्भित नहीं करता है।
केविन क्लाइन

2
व्यक्तिगत रूप से मुझे यह विधि पसंद है, क्योंकि यह आपको अपनी रणनीतियों को परस्पर डेटा के रूप में व्यवहार करने की अनुमति देता है , बजाय उन्हें अपरिवर्तनीय कोड के रूप में व्यवहार करने के।
ताकारॉय

2

"रणनीति" कम से कम एक बार वैकल्पिक एल्गोरिदम के बीच चयन करने के बारे में है , कम नहीं। कहीं आपके कार्यक्रम में किसी को निर्णय लेना है - शायद उपयोगकर्ता या आपका कार्यक्रम। यदि आप एक IoC, परावर्तन, एक डेटाफाइल मूल्यांकनकर्ता या एक स्विच / केस निर्माण का उपयोग करते हैं तो इसे लागू करने से स्थिति में बदलाव नहीं होता है।


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

@ वर्ड: निश्चित रूप से, मेरे जवाब को तदनुसार संपादित किया।
डॉक्टर ब्राउन

1

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

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


1

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

enum FactoryType {
   Type1(Type1.class),
   Type2(Type2.class);

   private Class<? extends Type> clazz;

   private FactoryType(Class<? extends Type> clazz) {
      this.clazz = clazz;
   }

   public Class<? extends Type> getTypeClass() {
      return clazz;
   }
}

यह फैक्ट्री कोड को काफी कम कर देता है:

public Type create(FactoryType type) throws Exception {
   return type.getTypeClass().newInstance();
}

(कृपया खराब त्रुटि से निपटने की उपेक्षा करें - उदाहरण कोड :))

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


यह निश्चित नहीं है कि इससे होम पेज पर क्या असर पड़ेगा लेकिन यह एक अच्छा जवाब है और जावा 8 में अब आप बिना रिफ्लेक्टर के कंस्ट्रक्टर संदर्भ बना सकते हैं।
जिमीजैम

1

प्रत्येक वर्ग के अपने प्रकार के प्रकार को परिभाषित करके संवेदनशीलता में सुधार किया जा सकता है। फिर आपका कारखाना उसी से मेल खाता है जो मेल खाता है।

उदाहरण के लिए:

public interface IOrderStrategy
{
    OrderType OrderType { get; }
}

public class NewOrder : IOrderStrategy
{
    public OrderType OrderType { get; } = OrderType.NewOrder;
}

public class OrderFactory
{
    private IEnumerable<IOrderStrategy> _strategies;

    public OrderFactory(IEnumerable<IOrderStrategy> strategies) // Injected by IoC container
    {
        _strategies = strategies;
    }

    public IOrderStrategy Create(OrderType orderType)
    {
        IOrderStrategy strategy = _strategies.FirstOrDefault(s => s.OrderType == orderType);

        if (strategy == null)
            throw new ArgumentException("Invalid order type.", nameof(orderType));

        return strategy;
    }
}

1

मुझे लगता है कि कितनी शाखाएँ स्वीकार्य हैं, इसकी एक सीमा होनी चाहिए।

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

किसी भी तरह से, आप इससे बच नहीं सकते हैं और कहीं न कहीं आपको उस स्थिति या प्रकार की जांच करनी होगी, जिसके साथ आप काम कर रहे हैं। फिर आप उसके लिए एक रणनीति बनाएंगे। चिंताओं की अच्छी पुरानी जुदाई।

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