2011-05-08 15 views
16

मुझे लगता है मैं यहाँ पहले से ही डि सवालों में से कुछ देखा है सीसी में निर्भरता इंजेक्शन कैसे करें?

में डि करने के लिए एक अच्छा तकनीकी समाधान के लिए देख रहा हूँ, लेकिन मैं किसी भी वास्तविक उदाहरण या ठोस कार्यान्वयन सुझावों के साथ एक नहीं देखा है।

तो, मान लीजिए कि हम निम्न स्थिति है:

हम ग में मॉड्यूल का एक सेट है; हम उन मॉड्यूल को दोबारा सुधारना चाहते हैं ताकि हम डीआई का उपयोग यूनिट परीक्षण चलाने के लिए कर सकें और इसी तरह।

प्रत्येक मॉड्यूल को प्रभावी ढंग से ग कार्यों का एक सेट के होते हैं:

module_function (...);

मॉड्यूल एक-दूसरे पर निर्भर करते हैं। अर्थात। आम तौर पर आपके पास एक कॉल हो सकता है जैसे:

int module1_doit(int x) { 
    int y = module2_dosomethingelse(x); 
    y += 2; 
    return(y); 
} 

इसके लिए DI के लिए सही दृष्टिकोण क्या है?

संभावित समाधान होने लगते हैं:

  • सभी मॉड्यूल कार्यों के लिए (1) का उपयोग करते हुए समारोह संकेत दिए गए हैं, और जब लागू एक समारोह इस (या समान) कार्य करें:

    पूर्णांक y = modules-> module2-> dosomethingelse (x);

  • (2) एक ही प्रतीकों के साथ कई पुस्तकालयों (नकली, std, आदि) संकलित करें और सही कार्यान्वयन में गतिशील रूप से लिंक करें।

(2) यह ऐसा करने का सही तरीका प्रतीत हो रहा है, लेकिन कॉन्फ़िगर करने के लिए मुश्किल है और annoyingly आप बलों प्रत्येक इकाई परीक्षण के लिए कई बाइनरी बनाने के लिए।

(1) लगता है जैसे कि यह काम हो सकता है, लेकिन कुछ बिंदु पर अपने डि नियंत्रक एक स्थिति है जहाँ आप गतिशील रूप से एक सामान्य कारखाने समारोह (शून्य ( कारखाने को लागू करने की जरूरत है में फंस जा रहा है) (...) कहें) कई अन्य मॉड्यूल के साथ जिन्हें रनटाइम पर इंजेक्शन दिया जाना चाहिए?

क्या सी में ऐसा करने का एक और बेहतर तरीका है?

ऐसा करने का 'सही' तरीका क्या है?

+8

उह। लोग अब बेवकूफ सी क्यों नहीं लिख सकते हैं? –

+1

बेवकूफ परिभाषित करें। – Skurmedel

+3

@ स्कुरमेडेल सी –

उत्तर

6

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

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

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

+0

+1 हटाएं। यह वास्तव में एकमात्र कारण समाधान प्रतीत होता है। – Doug

1

दो दृष्टिकोण हैं जिनका आप उपयोग कर सकते हैं। चाहे आप वास्तव में चाहते हैं या नहीं, क्योंकि राफ इंगित कर रहे हैं, आप पर निर्भर हैं।

पहला: स्थिर पुस्तकालय में "गतिशील रूप से" इंजेक्शन विधि बनाएं। पुस्तकालय के खिलाफ लिंक और परीक्षण के दौरान बस इसे प्रतिस्थापित करें। वोला, विधि बदल दी गई है।

दूसरा: सीधे शब्दों में पूर्व प्रसंस्करण के आधार पर संकलन समय प्रतिस्थापन प्रदान करते हैं:

#ifndef YOUR_FLAG 

    /* normal method versions */ 

#else 

    /* changed method versions */ 

#endif 

/* methods that have no substitute */ 
7

यह Ceedling के लिए एक आदर्श उपयोग-मामला है।

सिडलिंग एक छाता प्रोजेक्ट है जो एक साथ लाता है (अन्य चीजों के साथ) एकता और सीमॉक, जो एक साथ आप जो काम कर रहे हैं, उसे स्वचालित कर सकते हैं।

सामान्य सिडलिंग/यूनिटी/सीएमॉक में रूबी स्क्रिप्ट्स का एक सेट होता है जो आपके मॉड्यूल हेडर फ़ाइलों के आधार पर आपके कोड और ऑटो-जेनरेट मैक्स के माध्यम से स्कैन करता है, साथ ही परीक्षण धावक जो सभी परीक्षण ढूंढते हैं और रनर चलाते हैं उन्हें।

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

मैं शुरू में रूबी को परीक्षण के लिए हमारे निर्माण प्रणाली की निर्भरता के रूप में लाने में संकोच कर रहा था, और यह बहुत जटिलता और जादू की तरह लग रहा था, लेकिन ऑटो-जेनरेट किए गए मॉकिंग कोड का उपयोग करके कुछ परीक्षणों को लिखने और लिखने के बाद। झुका हुआ था

8

मैं सी में डि का उपयोग कर देखें के साथ किसी भी समस्या नहीं दिख रहा है:

http://devmethodologies.blogspot.com/2012/07/dependency-injection.html

+0

यह विषय पर एक अच्छी पोस्ट है। फंक्शन पॉइंटर्स एक अच्छा दृष्टिकोण की तरह लगते हैं; आपको केवल यह पता होना चाहिए कि लिंक-टाइम ऑप्टिमाइज़ेशन के बिना आप उनका उपयोग करने के लिए प्रदर्शन का थोड़ा सा हिट लेते हैं (एबीआई दृष्टिकोण को जोड़ने या उन प्रतीकों के एक विशिष्ट उदाहरण को गतिशील रूप से लोड करने के लिए जो आपको उपयोग करने की आवश्यकता है)। – Doug

2

एक छोटी सी इस पर पार्टी के लिए देर हो चुकी है, लेकिन इस हाल ही में एक विषय जहाँ मैं काम किया गया है।

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

बाद में एक अच्छा उदाहरण एफएटीएफएस है। http://elm-chan.org/fsw/ff/en/appnote.html

fatfs के लेखक पुस्तकालय कार्यों के थोक प्रदान करता है और (जैसे धारावाहिक परिधीय इंटरफेस कार्यों) लिखने के लिए पुस्तकालय के उपयोगकर्ता के लिए कुछ विशिष्ट निर्भरता relegates।

फ़ंक्शन पॉइंटर्स एक और उपयोगी टूल हैं, और टाइपपीफ का उपयोग करके कोड को बदसूरत होने से बचाने में मदद मिलती है।

यहाँ डिजिटल कनवर्टर (एडीसी) कोड के लिए मेरे एनालॉग से कुछ सरलीकृत के टुकड़े है:

typedef void (*adc_callback_t)(void); 

bool ADC_CallBackSet(adc_callback_t callBack) 
{ 
    bool err = false; 
    if (NULL == ADC_callBack) 
    { 
     ADC_callBack = callBack; 
    } 
    else 
    { 
     err = true; 
    } 
    return err; 
} 

// When the ADC data is ready, this interrupt gets called 
bool ADC_ISR(void) 
{ 
    // Clear the ADC interrupt flag 
    ADIF = 0; 

    // Call the callback function if set 
    if (NULL != ADC_callBack) 
    { 
     ADC_callBack(); 
    } 

    return true; // handled 
} 

// Elsewhere 
void FOO_Initialize(void) 
{ 
    ADC_CallBackSet(FOO_AdcCallback); 
    // Initialize other FOO stuff 
} 

void FOO_AdcCallback(void) 
{ 
    ADC_RESULT_T rawSample = ADC_ResultGet(); 
    FOO_globalVar += rawSample; 
} 

फू की बाधा व्यवहार अब एडीसी की बाधा सेवा दिनचर्या में इंजेक्ट किया जाता है।

आप इसे एक कदम आगे ले जा सकते हैं और FOO_Initialize में फ़ंक्शन पॉइंटर पास कर सकते हैं ताकि सभी निर्भरता मुद्दों को एप्लिकेशन द्वारा प्रबंधित किया जा सके।

//dependency_injection.h 
typedef void (*DI_Callback)(void) 
typedef bool (*DI_CallbackSetter)(DI_Callback) 

// foo.c 
bool FOO_Initialize(DI_CallbackSetter CallbackSet) 
{ 
    bool err = CallbackSet(FOO_AdcCallback); 
    // Initialize other FOO stuff 
    return err; 
} 
+0

लेकिन इस मामले में एडीसी इंटरप्ट अवरुद्ध है जब तक कॉलबैक फ़ंक्शन नहीं कहा जाता है। यदि कॉल बैक खुद को एक और कॉलबैक कॉल करता है तो यह आईएसआर लंबा कर देगा। – prasad

+0

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

संबंधित मुद्दे