एक वर्ग के लिए पैटर्न जो केवल एक ही काम करता है


24

मान लीजिए कि मेरे पास एक प्रक्रिया है जो सामान बनाती है :

void doStuff(initalParams) {
    ...
}

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

class StuffDoer {
    private someInternalState;

    public Start(initalParams) {
        ...
    }

    // some private helper procedures here
    ...
}

और फिर मैं इसे इस तरह से कॉल करता हूं:

new StuffDoer().Start(initialParams);

या इस तरह:

new StuffDoer(initialParams).Start();

और यही गलत लगता है। .NET या जावा एपीआई का उपयोग करते समय, मैं हमेशा कॉल नहीं new SomeApiClass().Start(...);करता हूं, जिससे मुझे संदेह होता है कि मैं इसे गलत कर रहा हूं। ज़रूर, मैं StuffDoer के निर्माता को निजी बना सकता हूं और एक स्थिर सहायक विधि जोड़ सकता हूं:

public static DoStuff(initalParams) {
    new StuffDoer().Start(initialParams);
}

लेकिन फिर मेरे पास एक ऐसा वर्ग होगा, जिसके बाहरी इंटरफ़ेस में केवल एक स्टैटिक विधि होती है, जो अजीब भी लगता है।

इसलिए मेरा प्रश्न: क्या इस प्रकार की कक्षाओं के लिए एक अच्छी तरह से स्थापित पैटर्न है जो

  • केवल एक प्रवेश बिंदु है और
  • कोई "बाहरी रूप से पहचाने जाने योग्य" राज्य नहीं है, अर्थात, उदाहरण प्रविष्टि के निष्पादन के दौरान केवल उदाहरण स्थिति की आवश्यकता होती है ?

मुझे उन चीजों को करने के लिए जाना जाता है जैसे कि bool arrayContainsSomestring = new List<string>(stringArray).Contains("somestring");जब मैंने ध्यान रखा था कि जानकारी का एक विशेष टुकड़ा और LINQ विस्तार विधियाँ उपलब्ध नहीं हैं। ठीक काम करता है, और if()हुप्स के माध्यम से कूदने की आवश्यकता के बिना एक शर्त के अंदर फिट बैठता है । यदि आप उस तरह का कोड लिख रहे हैं, तो बेशक, आप एक कचरा एकत्र भाषा चाहते हैं।
बजे एक सीवीएन

यदि यह भाषा-अज्ञेयवादी है , तो java और c # को भी क्यों जोड़ें ? :)
स्टीवन ज्यूरिस

मैं उत्सुक हूँ, इस 'एक विधि' की कार्यक्षमता क्या है? यह निर्धारित करने में बहुत हद तक मदद करेगा कि विशिष्ट परिदृश्य में सबसे अच्छा दृष्टिकोण क्या है, लेकिन फिर भी दिलचस्प सवाल!
स्टीवन ज्यूरिस

@StevenJeuris: मैंने विभिन्न स्थितियों में इस मुद्दे पर ठोकर खाई है, लेकिन वर्तमान मामले में यह एक विधि है जो स्थानीय डेटाबेस में डेटा आयात करती है (इसे वेब सर्वर से लोड करती है, कुछ जोड़तोड़ करती है और इसे डेटाबेस में संग्रहीत करती है)।
Heinzi

1
यदि आप उदाहरण बनाते समय हमेशा विधि को कॉल करते हैं, तो इसे निर्माणकर्ता के अंदर एक प्रारंभिक तर्क क्यों नहीं बनाते हैं?
NoChance 12

जवाबों:


29

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

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


1
यह बिल्कुल वैसा ही लगता है जैसा मैं कर रहा हूं। धन्यवाद, कम से कम मेरे पास अभी इसके लिए एक नाम है। :-)
हेंजी

2
बहुत प्रासंगिक लिंक! यह मेरे लिए एक विरोधी पैटर्न की तरह गंध करता है। यदि आपका तरीका इतना लंबा है, तो शायद इसके हिस्सों को पुन: प्रयोज्य कक्षाओं में निकाला जा सकता है या आमतौर पर बेहतर तरीके से रिफैक्ट किया जा सकता है? मैंने पहले भी इस पैटर्न के बारे में नहीं सुना है, न ही मुझे कई अन्य संसाधन मिल सकते हैं जो मुझे विश्वास दिलाते हैं कि यह आमतौर पर उतना उपयोग नहीं किया जाता है।
स्टीवन ज्यूरिस


1
@StevenJeuris मुझे नहीं लगता कि यह एक विरोधी पैटर्न है। एक एंटी-पैटर्न इस अवस्था को संचय करने के बजाय कई मापदंडों के साथ रिफैक्ट किए गए तरीकों को अव्यवस्थित करने के लिए होगा जो इन तरीकों के बीच एक उदाहरण चर में आम है।
अल्फ्रेडो ओसोरियो

@AlfredoOsorio: ठीक है, यह बहुत सारे मापदंडों के साथ अपवर्तित तरीकों को अव्यवस्थित करता है। वे ऑब्जेक्ट में रहते हैं, इसलिए यह उन सभी को पास करने से बेहतर है, लेकिन यह पुन: प्रयोज्य घटकों के लिए फिर से तैयार करने से भी बदतर है जो केवल प्रत्येक पैरामीटर के एक सीमित सबसेट की आवश्यकता है।
Jan Hudec

13

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

के लिए एक ही बात है no "externally recognizable" state। यह अच्छा एनकैप्सुलेशन है।

मुझे कोड जैसी कोई समस्या नहीं दिख रही है:

var doer = new StuffDoer(initialParams);
var result = doer.Calculate(extraParams);

1
और हां, अगर आपको doerफिर से उसी का उपयोग करने की आवश्यकता नहीं है , तो यह कम हो जाएगा var result = new StuffDoer(initialParams).Calculate(extraParams);
बजे एक सीवीएन

गणना करने के लिए doStuff का नाम बदला जाना चाहिए!
शबूनक

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

2
यह उत्तर अत्यंत सरल है। एक StringComparerवर्ग है जो आप के रूप में new StringComparer().Compare( "bleh", "blah" )अच्छी तरह से डिजाइन का उपयोग करें ? जब पूरी तरह से कोई ज़रूरत नहीं है तो राज्य बनाने के बारे में क्या? ठीक है व्यवहार बहुत अच्छी तरह से समझाया गया है (ठीक है, कम से कम वर्ग के उपयोगकर्ता के लिए, उस पर मेरे जवाब में अधिक ), लेकिन यह डिफ़ॉल्ट रूप से इसे 'अच्छा डिजाइन' नहीं बनाता है। अपने 'कर्ता-परिणाम' उदाहरण के रूप में। यह केवल तभी समझ में आएगा जब आप कभी इससे doerअलग प्रयोग करेंगे result
स्टीवन ज्यूरिस

1
यह अस्तित्व का एक कारण नहीं है, यह है one reason to change। अन्तर सूक्ष्म लग सकता है। आपको समझना होगा कि इसका क्या मतलब है। यह कभी भी एक तरीके की कक्षाएं नहीं बनाएगा
jgauffin

8

एक ठोस सिद्धांतों के दृष्टिकोण से jgauffin का उत्तर समझ में आता है। हालाँकि, आपको सामान्य डिज़ाइन सिद्धांतों जैसे जानकारी छिपाना नहीं भूलना चाहिए ।

मुझे दिए गए दृष्टिकोण के साथ कई समस्याएं हैं:

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

कुछ संबंधित संदर्भ

संभवतः तर्क का एक मुख्य बिंदु यह है कि सिंगल रिस्पॉन्सिबिलिटी प्रिंसिपल को कैसे आगे बढ़ाया जाए"यदि आप इसे उस चरम पर ले जाते हैं और ऐसी कक्षाएं बनाते हैं जिनका अस्तित्व होने का एक कारण है, तो आप प्रति कक्षा केवल एक विधि के साथ समाप्त हो सकते हैं। इससे कक्षाओं का एक बड़ा फैलाव और भी सरल प्रक्रियाओं के लिए पैदा होगा, जिससे सिस्टम हो सकता है। समझना मुश्किल है और बदलना मुश्किल है। ”

इस विषय से संबंधित एक अन्य प्रासंगिक संदर्भ: "अपने कार्यक्रमों को उन तरीकों में विभाजित करें जो एक पहचानने योग्य कार्य करते हैं। सभी कार्यों को एक ही स्तर पर अमूर्त स्तर पर रखें ।" - केंट बेक की यहाँ "अमूर्त के समान स्तर" है। इसका मतलब यह नहीं है कि "एक बात", क्योंकि इसकी व्याख्या अक्सर की जाती है। अमूर्त का यह स्तर पूरी तरह से उस संदर्भ तक है , जिसके लिए आप डिजाइन कर रहे हैं।

तो उचित दृष्टिकोण क्या है?

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

new TupleList<Key, int>
{
    { Key.NumPad1, 1 },
            ...
    { Key.NumPad3, 16 },
    { Key.NumPad4, 17 },
}
    .ForEach( t =>
    {
        var trigger = new IC.Trigger.EventTrigger(
                        new KeyInputCondition( t.Item1, KeyInputCondition.KeyState.Down ) );
        trigger.ConditionsMet += () => AddMarker( t.Item2 );
        _inputController.AddTrigger( trigger );
    } );

चूँकि बहुत 'स्थानीय' कोड का ForEachपुन: उपयोग कहीं और नहीं किया गया है, इसलिए मैं इसे केवल उसी स्थान पर रख सकता हूँ जहाँ यह प्रासंगिकता का है। आउटलाइनिंग कोड इस तरह से कि कोड जो एक दूसरे पर निर्भर करता है, दृढ़ता से एक साथ समूहीकृत होता है, यह मेरी राय में अधिक पठनीय बनाता है।

संभव विकल्प

  • C # में आप इसके बजाय एक्सटेंशन विधियों का उपयोग कर सकते हैं। तो तर्क पर काम करते हैं सीधे आप इस 'एक बात' विधि से गुजरते हैं।
  • देखें कि क्या यह फ़ंक्शन वास्तव में किसी अन्य वर्ग से संबंधित नहीं है।
  • इसे एक स्थिर वर्ग में एक स्थिर कार्य करें । यह सबसे अधिक संभावना सबसे उपयुक्त दृष्टिकोण है, जैसा कि आपके द्वारा संदर्भित सामान्य एपीआई में भी परिलक्षित होता है।

मुझे इस एंटी-पैटर्न के लिए एक संभावित नाम मिला जैसा कि एक अन्य उत्तर में, पोल्टरजिस्ट्स ने बताया
स्टीवन ज्यूरिस

4

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


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

2
new StuffDoer().Start(initialParams);

क्लूलेस डेवलपर्स के साथ एक समस्या है जो एक साझा उदाहरण का उपयोग कर सकते हैं और इसका उपयोग कर सकते हैं

  1. कई बार (ठीक है अगर पिछले (शायद आंशिक) निष्पादन बाद के निष्पादन को गड़बड़ नहीं करता है)
  2. कई थ्रेड्स से (ठीक नहीं है, आपने स्पष्ट रूप से कहा है कि इसमें आंतरिक स्थिति है)।

तो यह स्पष्ट दस्तावेज की आवश्यकता है कि यह थ्रेड सुरक्षित नहीं है और यदि यह हल्का है (इसे बनाना तेज है, तो बाहरी संसाधनों को न तो बाँधें और न ही बहुत सारी मेमोरी) यह मक्खी पर पलटने के लिए ठीक है।

स्थैतिक विधि में इसे छिपाने से इसमें मदद मिलती है, क्योंकि तब इसका पुन: उपयोग कभी नहीं होता है।

यदि यह कुछ महंगा प्रारंभ है, यह कर सकता है (यह हमेशा नहीं है) यह एक बार आरंभ और यह एक और वर्ग है कि केवल राज्य होते हैं और यह cloneable होगा बनाने के द्वारा कई बार का उपयोग कर तैयार करने के लिए फायदेमंद हो। प्रारंभ में वह स्थिति बनेगी जिसमें संग्रहीत किया जाएगा doerstart()इसे क्लोन करके आंतरिक तरीकों से पास किया जाएगा।

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


अच्छे अंक। आशा है कि आपको सफाई से कोई आपत्ति नहीं है।
स्टीवन ज्यूरिस

2

इसके अलावा मेरी और अधिक व्यापक, व्यक्तिगत अन्य जवाब , मैं निम्नलिखित अवलोकन एक अलग जवाब हकदार लग रहा है।

ऐसे संकेत हैं कि आप पॉलीटर्जिस्ट विरोधी पैटर्न का अनुसरण कर रहे हैं ।

Poltergeists बहुत सीमित भूमिकाओं और प्रभावी जीवन चक्र वाली कक्षाएं हैं। वे अक्सर अन्य वस्तुओं के लिए प्रक्रिया शुरू करते हैं। परावर्तित समाधान में लंबे समय तक जीवित वस्तुओं के लिए जिम्मेदारियों का एक पुनर्विकास शामिल है जो पॉलीटर्जिस्ट को समाप्त करते हैं।

लक्षण और परिणाम

  • निरर्थक नेविगेशन पथ।
  • क्षणिक संगति।
  • स्टेटलेस क्लास।
  • अस्थायी, छोटी अवधि की वस्तुएं और कक्षाएं।
  • एकल-संचालन वर्ग जो केवल "बीज" या अस्थायी संगठनों के माध्यम से अन्य वर्गों को "आह्वान" करने के लिए मौजूद हैं।
  • "कंट्रोल-लाइक" ऑपरेशन नाम वाली कक्षाएं जैसे start_process_alpha।

रिफैक्टर्ड सॉल्यूशन

घोस्टबस्टर्स पूरी तरह से कक्षा पदानुक्रम से हटाकर पॉलीटर्ज़िस्ट को हल करते हैं। उनके हटाने के बाद, हालांकि, पॉलीजिस्ट द्वारा "प्रदान" की गई कार्यक्षमता को प्रतिस्थापित किया जाना चाहिए। वास्तुकला को सही करने के लिए एक सरल समायोजन के साथ यह आसान है।


0

मार्टिन फाउलर के EAA सूचीपत्र के P पर कई पैटर्न मिल सकते हैं ;

  1. लेन-देन स्क्रिप्ट : आपके द्वारा उल्लिखित दृष्टिकोण के समान।
  2. विधि w / विधि ऑब्जेक्ट को बदलें : एक अच्छा तरीका भी देखें जैसे @anon उल्लेख किया गया है।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.