2011-10-18 21 views
5

सी ++ में मैं अक्सर कोड को और अधिक विश्वसनीय बनाने के लिए रैली-स्टाइल ऑब्जेक्ट्स का उपयोग करता हूं और कोड को अधिक प्रदर्शन करने के लिए स्टैक पर आवंटित करता हूं (और bad_alloc से बचने के लिए)।आवंटित आरएआईआई ऑब्जेक्ट बनाम डी सिद्धांत

लेकिन स्टैक पर कंक्रीट क्लास का एक ऑब्जेक्ट बनाना निर्भरता उलटा (डीआई) सिद्धांत का उल्लंघन करता है और इस ऑब्जेक्ट को मजाक करने से रोकता है।

निम्नलिखित कोड पर विचार करें:

struct IInputStream 
{ 
    virtual vector<BYTE> read(size_t n) = 0; 
}; 

class Connection : public IInputStream 
{ 
public: 
    Connection(string address); 
    virtual vector<BYTE> read(size_t n) override; 
}; 

struct IBar 
{ 
    virtual void process(IInputStream& stream) = 0; 
}; 

void Some::foo(string address, IBar& bar) 
{ 
    onBeforeConnectionCreated(); 
    { 
     Connection conn(address); 
     onConnectionCreated(); 
     bar.process(conn); 
    } 
    onConnectionClosed(); 
} 

मैं IBar::process परीक्षण कर सकते हैं, लेकिन मैं यह भी Some::foo परीक्षण करने के लिए, वास्तविक कनेक्शन वस्तु बनाने के बिना चाहते हैं।

निश्चित रूप से मैं एक कारखाने का उपयोग कर सकता हूं, लेकिन यह कोड को काफी जटिल करेगा और ढेर आवंटन पेश करेगा।
इसके अलावा, मैं Connection::open विधि जोड़ने की इच्छा नहीं करता, मैं पूरी तरह से प्रारंभिक और पूरी तरह कार्यात्मक वस्तुओं का निर्माण करना पसंद करता हूं।

मैं Some के लिए Connection प्रकार टेम्पलेट पैरामीटर बनाना होगा (या foo के लिए करता है, तो एक नि: शुल्क समारोह के रूप में यह निकालने), लेकिन मुझे यकीन है कि यह सही तरीके से (टेम्पलेट्स कई लोगों के लिए एक काला जादू की तरह लग रही है, इसलिए मैं नहीं कर रहा हूँ गतिशील polymorphism का उपयोग करना पसंद करते हैं)

+2

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

+4

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

+1

आरएआईआई और डीआई एक साथ काम करते हैं, इसलिए शीर्षक भ्रामक है, आपकी समस्या स्टैक आवंटन बनाम डी है। –

उत्तर

5

आप अभी क्या कर रहे हैं "आरएआईआई कक्षा" और सेवा प्रदाता वर्ग (जो, यदि आप टेस्टेबिलिटी चाहते हैं, तो वास्तव में इसके बजाय एक इंटरफ़ेस होना चाहिए)। में IConnection

  • एक अलग ScopedConnection वर्ग कि कि
  • के शीर्ष उदाहरण के लिए पर आरए II प्रदान करता है

    1. Connection सार संक्षेप: द्वारा इस पते

      void Some::foo(string address, IBar& bar) 
      { 
          onBeforeConnectionCreated(); 
          { 
           ScopedConnection conn(this->pFactory->getConnection()); 
           onConnectionCreated(); 
           bar.process(conn); 
          } 
          onConnectionClosed(); 
      } 
      
    +2

    और स्वीकार करें कि 'ScopedConnection' को मॉक करने की आवश्यकता नहीं है, यह वास्तविक संस्करण का उपयोग करने के लिए "सुरक्षित" है, यहां तक ​​कि' कुछ :: foo' को अलग करने वाले परीक्षणों में भी। या यदि यह अस्वीकार्य है, तो अपने दांतों को ग्रिट करें और इसे टेम्पलेट पैरामीटर के रूप में इंजेक्ट करें, या RAII प्रदान करने के लिए 'scoped_ptr' का उपयोग करें, जो एक मानक वर्ग (या तृतीय पक्ष यदि आप अभी भी C++ 03 पर हैं) एक स्वीकार्य कठिन है निर्भरता। –

    +0

    कारखाने के बारे में मैंने यही लिखा है। अपने उत्तर का पालन करने के लिए, मुझे केवल कनेक्शन के लिए कारखाना बनाना चाहिए, या कई असंबद्ध वर्गों के लिए फैक्ट्री बनाना चाहिए (जैसा कि आप सुझाव देते हैं)। इस कारखाने को कई परतों के माध्यम से 'कुछ' तक लाएं (या इसे वैश्विक बनाएं)। – Abyx

    +0

    @Abyx: कारखाना DI के लिए एक प्रमुख उम्मीदवार होगा, जो मैन्युअल रूप से या वैश्विक होने के माध्यम से इसे पारित करने के लिए बेहतर होगा। लेकिन आपको अमूर्तता बढ़ाने के लिए इसकी आवश्यकता है। – Jon

    1

    "मैं उपयोग कर सकते हैं एक कारखाना है, लेकिन यह संकेतक रूप से कोड को जटिल करेगा और ढेर आवंटन शुरू करेगा "मेरा मतलब निम्न चरणों का था:

    सार कक्षा बनाएं और से यह

    struct AConnection : IInputStream 
    { 
        virtual ~AConnection() {} 
    }; 
    

    कारखाने जोड़ें विधि के लिए Some

    class Some 
    { 
    ..... 
    protected: 
        VIRTUAL_UNDER_TEST AConnection* createConnection(string address); 
    }; 
    

    Connection निकाले जाते हैं स्मार्ट सूचक

    unique_ptr<AConnection> conn(createConnection(address)); 
    
    1

    अपने वास्तविक बीच चुना करने के लिए स्टैक-आवंटित connecton बदलें कार्यान्वयन और मजाकिया, आपको वास्तविक ty इंजेक्ट करना होगा पे कि आप कुछ फैशन में निर्माण करना चाहते हैं। जिस तरह से मैं अनुशंसा करता हूं वह वैकल्पिक टेम्पलेट पैरामीटर के रूप में टाइप इंजेक्शन कर रहा है। यह आपको Some::foo का उपयोग करने के लिए अनावश्यक रूप से उपयोग करने की अनुमति देता है, लेकिन परीक्षण के मामले में बनाए गए कनेक्शन को स्वैप करने में सक्षम बनाता है।

    template<typename ConnectionT=Connection> // models InputStream 
    void Some::foo(string address, IBar& bar) 
    { 
        onBeforeConnectionCreated(); 
        { 
         ConnectionT conn(address); 
         onConnectionCreated(); 
         bar.process(conn); 
        } 
        onConnectionClosed(); 
    } 
    

    यदि आप संकलन समय पर वास्तविक प्रकार को जानते हैं तो मैं कारखाने और रनटाइम बहुरूपता का ऊपरी भाग नहीं बनाऊंगा।

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