उच्च-क्रम के कार्यों से निर्भरता उलटा कैसे होती है?


41

आज मैंने सिर्फ इस लेख को देखा है जिसमें एफ # विकास में ठोस सिद्धांत की प्रासंगिकता का वर्णन किया गया है-

एफ # और डिजाइन सिद्धांत - एसओएलआईडी

और अंतिम एक को संबोधित करते हुए - "निर्भरता उलटा सिद्धांत", लेखक ने कहा:

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

लेकिन उन्होंने आगे इसकी व्याख्या नहीं की। तो, मेरा प्रश्न यह है कि उच्च क्रम के कार्यों पर निर्भरता उलटा कैसे होती है?

जवाबों:


38

ओओपी में निर्भरता उलटने का मतलब है कि आप एक इंटरफ़ेस के खिलाफ कोड करते हैं जो किसी ऑब्जेक्ट में कार्यान्वयन द्वारा प्रदान किया जाता है।

उच्च भाषा के कार्यों का समर्थन करने वाली भाषाएं अक्सर एक वस्तु के बजाय एक फ़ंक्शन के रूप में व्यवहार को पारित करके सरल निर्भरता उलटा समस्याओं को हल कर सकती हैं जो ओओ-अर्थ में एक इंटरफ़ेस को लागू करती है।

ऐसी भाषाओं में, फ़ंक्शन का हस्ताक्षर इंटरफ़ेस बन सकता है और वांछित व्यवहार प्रदान करने के लिए एक पारंपरिक ऑब्जेक्ट के बजाय एक फ़ंक्शन पारित किया जाता है। मध्य पैटर्न में छेद इसके लिए एक अच्छा उदाहरण है।

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

C # में एक उदाहरण

परंपरागत दृष्टिकोण:

public IEnumerable<Customer> FilterCustomers(IFilter<Customer> filter, IEnumerable<Customers> customers)
{
    foreach(var customer in customers)
    {
        if(filter.Matches(customer))
        {
            yield return customer;
        }
    }
}

//now you've got to implement all these filters
class CustomerNameFilter : IFilter<Customer> /*...*/
class CustomerBirthdayFilter : IFilter<Customer> /*...*/

//the invocation looks like this
var filteredDataByName = FilterCustomers(new CustomerNameFilter("SomeName"), customers);
var filteredDataBybirthDay = FilterCustomers(new CustomerBirthdayFilter(SomeDate), customers);

उच्च आदेश कार्यों के साथ:

public IEnumerable<Customer> FilterCustomers(Func<Customer, bool> filter, IEnumerable<Customers> customers)
{
    foreach(var customer in customers)
    {
        if(filter(customer))
        {
            yield return customer;
        }
    }
}

अब कार्यान्वयन और आह्वान कम बोझिल हो जाते हैं। हमें अब एक IFilter कार्यान्वयन की आपूर्ति करने की आवश्यकता नहीं है। हमें अब फ़िल्टर के लिए कक्षाएं लागू करने की आवश्यकता नहीं है।

var filteredDataByName = FilterCustomers(x => x.Name.Equals("CustomerName"), customers);
var filteredDataByBirthday = FilterCustomers(x => x.Birthday == SomeDateTime, customers);

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


3
अच्छा उदाहरण है। हालाँकि, गुलशन की तरह मैं कार्यात्मक प्रोग्रामिंग के बारे में और अधिक जानने की कोशिश कर रहा हूँ और मैं सोच रहा था कि क्या "कार्यात्मक DI" इस तरह की वस्तु "ओरिएंटेड डीआई" की तुलना में कुछ कठोरता और महत्व का त्याग नहीं करता है। उच्च आदेश हस्ताक्षर केवल राज्यों समारोह पारित कर एक पैरामीटर के रूप में एक ग्राहक लेने के लिए और जबकि OO संस्करण एक bool लौटना चाहिए तथ्य यह है कि वस्तु पारित कर लागू करता है कि है एक फिल्टर (औजार IFilter <ग्राहक>)। यह फ़िल्टर की धारणा को भी स्पष्ट करता है, जो कि डोमेन की एक मुख्य अवधारणा है (DDD देखें) तो यह एक अच्छी बात हो सकती है। तुम क्या सोचते हो ?
गुरिल्ला

2
@ ian31: यह वास्तव में एक दिलचस्प विषय है! FilterCustomer को जो कुछ भी पारित किया जाता है, वह कुछ प्रकार के फिल्टर के रूप में व्यवहार करेगा। जब फ़िल्टर कॉन्सेप्ट डोमेन का एक अनिवार्य हिस्सा होता है और आपको जटिल फ़िल्टर नियमों की आवश्यकता होती है जो कि सिस्टम के पार कई बार उपयोग किए जाते हैं, तो उनका एनकैप्सुलेट करना बेहतर होता है। यदि नहीं या केवल बहुत कम डिग्री के लिए, तो मैं तकनीकी सादगी और व्यावहारिकता के लिए लक्ष्य बनाता हूं।
फाल्कन

5
@ ian31: मैं पूरी तरह से असहमत हूं। लागू IFilter<Customer>करना कोई प्रवर्तन नहीं है। उच्च-क्रम फ़ंक्शन अपेक्षाकृत अधिक लचीला है, जो एक बड़ा लाभ है, और उन्हें इनलाइन लिखने में सक्षम होना एक और बहुत बड़ा लाभ है। लैम्ब्डा स्थानीय चर को पकड़ने में बहुत आसान है।
डेडएमजी

3
@ ian31: फ़ंक्शन को संकलन-समय पर भी सत्यापित किया जा सकता है। आप एक फ़ंक्शन भी लिख सकते हैं, इसे नाम दे सकते हैं, और तब तक इसे एक तर्क के रूप में पास कर सकते हैं जब तक यह स्पष्ट अनुबंध को पूरा करता है (ग्राहक को रिटर्न बूल लेता है)। जरूरी नहीं कि आप एक लंबोदर अभिव्यक्ति पास करें। तो आप कुछ हद तक स्पष्टता की कमी को कवर कर सकते हैं। हालांकि, अनुबंध और इसकी मंशा स्पष्ट रूप से व्यक्त नहीं की गई है। यह एक बड़ा नुकसान है। सब में यह स्पष्टता, भाषा और encapsulation की बात है। मुझे लगता है कि आपको प्रत्येक मामले को स्वयं ही आंकना होगा।
फाल्कन

2
यदि आप एक इंजेक्शन फ़ंक्शन के अर्थ को स्पष्ट करने के बारे में दृढ़ता से महसूस करते हैं, तो आप प्रतिनिधियों का उपयोग करके C # नाम फ़ंक्शन हस्ताक्षर कर सकते हैं public delegate bool CustomerFilter(Customer customer):। शुद्ध कार्यात्मक भाषाओं में जैसे हैस्केल, अलियासिंग प्रकार तुच्छ है:type customerFilter = Customer -> Bool
सारा

8

यदि आप किसी फ़ंक्शन का व्यवहार बदलना चाहते हैं

doThis(Foo)

आप एक और समारोह पारित कर सकते हैं

doThisWith(Foo, anotherFunction)

जो व्यवहार आप अलग करना चाहते हैं उसे लागू करता है।

"doThisWith" एक उच्च-क्रम फ़ंक्शन है क्योंकि यह एक तर्क के रूप में एक और फ़ंक्शन लेता है।

उदाहरण के लिए आप हो सकते हैं

storeValues(Foo, writeToDatabase)
storeValues(Foo, imitateDatabase)

5

संक्षिप्त जवाब:

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

इंटरफ़ेस / ClassImplementation के बजाय कई निर्भरताएँ एक प्रतिनिधि फ़ंक्शन के साथ आसान रूप से कार्यान्वित की जा सकती हैं।

आप ioc-factory-pros-&-contras-for-interface-vs-प्रतिनिधियों में c # दोनों के लिए एक उदाहरण पाते हैं ।


0

इसकी तुलना करें:

String[] names = {"Fred", "Susan"};
List<String> namesBeginningWithS = new LinkedList<String>();
for (String name : names) {
    if (name.startsWith("S")) {
        namesBeginningWithS.add(name);
    }
}

साथ में:

String[] names = {"Fred", "Susan"};
List<String> namesBeginningWithS = names.stream().filter(n <- n.startsWith("S")).collect();

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


0

LennyProgrammers उदाहरण के पिगी-बैकिंग ...

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

यदि इसके बजाय:

doThisWith(Foo, anotherFunction)

हम (पारंपरिक रूप से पीएफए ​​कैसे किया जाता है) में निम्न स्तरीय कार्यकर्ता कार्य (स्वैपिंग आर्ग ऑर्डर) के रूप में होते हैं:

doThisWith( anotherFunction, Foo )

हम तो आंशिक रूप से लागू कर सकते हैंइस तरह से:

doThis = doThisWith( anotherFunction )  // note that "Foo" is still missing, argument list is partial

जो हमें बाद में नए फ़ंक्शन का उपयोग करने की अनुमति देता है जैसे:

doThis(Foo)

या और भी:

doThat = doThisWith( yetAnotherDependencyFunction )
...
doThat( Bar )

इसे भी देखें: https://ramdajs.com/docs/#partial

... और, हाँ, योजक / गुणक अकल्पनीय उदाहरण हैं। एक बेहतर उदाहरण एक ऐसा फ़ंक्शन होगा जो संदेश लेता है, और उन्हें लॉग करता है या उन पर निर्भर करता है कि "उपभोक्ता" फ़ंक्शन निर्भरता के रूप में पारित किया गया है।

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

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

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