2010-07-07 15 views
5

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

class Client { 
public: 
    Client(); 
    void sendStuff(const Stuff &stuff) {_pimpl->sendStuff(stuff);} 
    Stuff getStuff(const StuffId &id) {return _pimpl->getStuff(id);} 
private: 
    ClientImpl *_pimpl; 
} 

class ClientImpl { // not exported 
public: 
    void sendStuff(const Stuff &stuff); 
    Stuff getStuff(const StuffId &id); 
private: 
    Connection _connection; 
} 

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

class ClientInterface { 
public: 
    void sendStuff(const Stuff &stuff) = 0; 
    Stuff getStuff(const StuffId &id) = 0; 
} 

class ClientImplementation : public ClientInterface { // not exported 
public: 
    ClientImplementation(Connection *connection); 
    // +implementation of ClientInterface 
} 

class ClientFactory { 
    static ClientInterface *create(); 
} 

वहाँ कोई कारण इस स्थिति में pimpl साथ जाने के लिए कर रहे हैं?

+0

मैं गलत हो सकता हूं, लेकिन यदि आपके पास 'कनेक्शन' (और 'कनेक्शन *' प्रकार) का सदस्य नहीं है, तो आपको अपनी शीर्षलेख में अपनी परिभाषा शामिल करनी होगी और इसलिए 'कनेक्शन' आपकी लाइब्रेरी के उपयोगकर्ताओं को ज्ञात है। – ereOn

+0

http: // stackoverflow देखें।कॉम/प्रश्न/825018/pimpl-idiom-vs-pure-virtual-class-interface –

+0

@ereOn: क्लाइंट हेडर में मैं क्लाइंटइम्पल क्लास की केवल अगली घोषणा का उपयोग करता हूं (यह संभव है, क्योंकि सदस्य एक सूचक है) इसलिए क्लाइंटइम्प्लर हेडर हो सकता है मेरे लाइब्रेरी क्लाइंट से छिपा हुआ है, इसलिए मैं ClientImpl के सदस्य के रूप में कनेक्शन का उपयोग कर सकता हूं। – chalup

उत्तर

1

GoTW15

GoTW28

हर्ब Sutter से। आपको शुरू करने के लिए अच्छे संकेतक।

4

AFAIK Pimpl idiom का उपयोग करने का सामान्य कारण वर्ग के कार्यान्वयन के लिए संकलन/लिंक समय निर्भरताओं को कम करना है (सार्वजनिक हेडर फ़ाइल से कार्यान्वयन विवरण को हटाकर)। एक अन्य कारण कक्षा को अपने व्यवहार को गतिशील रूप से बदलने में सक्षम होना चाहिए (उर्फ State pattern)।

दूसरा ऐसा मामला प्रतीत नहीं होता है, और पहला विरासत + कारखाने के साथ भी हासिल किया जा सकता है। हालांकि, जैसा कि आपने नोट किया है, बाद वाला समाधान इकाई परीक्षण के लिए बहुत आसान है, इसलिए मैं इसे पसंद करूंगा।

+0

पिंपल मुहावरे का उपयोग करने का एक अन्य कारण कक्षा के सार्वजनिक इंटरफ़ेस से कार्यान्वयन विवरण को अलग करना है: केवल सार्वजनिक सदस्य कक्षा शीर्षलेख में दिखाई दे रहे हैं। –

+1

पिंपल मुहावरे का उपयोग करने का एकमात्र कारण यह है कि जब आप सार आधार वर्ग का उपयोग नहीं कर सकते हैं। उदाहरण के लिए जब आप कक्षा को कार्यान्वित करते हैं जिसका मतलब स्टैक पर मूल्य या किसी अन्य वर्ग के सदस्य के रूप में तत्काल किया जाता है। उस परिस्थिति में आप अभी भी 'संकलन फ़ायरवॉल' कर सकते हैं धन्यवाद pimpl। –

+0

यदि मैं केवल अमूर्त इंटरफ़ेस + फैक्ट्री निर्यात करता हूं, तो क्या यह संकलन/लिंक समय निर्भरताओं को भी कम नहीं करता है? – chalup

0

हां यह पिंपल पैटर्न का उपयोग करने के लिए एक अच्छी जगह है, और हाँ यह परीक्षण करना मुश्किल होगा।

  • Pimpl ग्राहक से निर्भरता छुपा के बारे में है:

    समस्या यह है कि दो अवधारणाओं को एक-दूसरे का विरोध करते है इस संकलन/लिंक समय कम कर देता है और देखने का एक ABI स्थिरता बिंदु से बेहतर है।

  • यूनिट टेस्टिंग निर्भरता में शल्य चिकित्सा हस्तक्षेप (नकली अप के उपयोग, उदाहरण के लिए)

हालांकि, इसका मतलब यह नहीं कि आप किसी अन्य के लिए एक बलिदान चाहिए के बारे में आम तौर पर है। इसका मतलब केवल आपको अपना कोड अनुकूलित करना चाहिए।

अब, Connection को उसी मुहावरे के साथ लागू किया गया था तो क्या होगा?

class Connection 
{ 
private: 
    ConnectionImpl* mImpl; 
}; 

और एक फैक्टरी के माध्यम से दिया:

// Production code: 

Client client = factory.GetClient(); 

// Test code: 
MyTestConnectionImpl impl; 
Client client = factory.GetClient(impl); 

इस तरह, आप जुओं से भरा हुआ किरकिरा अपने परीक्षण के विवरण कनेक्शन के लागू ग्राहक के लिए कार्यान्वयन उजागर या तोड़ने ABI बिना ग्राहक का परीक्षण करते समय उपयोग कर सकते हैं ।

+0

समस्या यह है कि खाद्य श्रृंखला वाले लोग किसी भी प्रकार की फैक्ट्री विधि/कक्षा नहीं चाहते हैं और न ही किसी भी रचनाकार जो निर्भरता लेते हैं - केवल क्लाइंट :: क्लाइंट() कन्स्ट्रक्टर होना चाहिए ... – chalup

+0

इस मामले में आप इसका उपयोग कर सकते हैं सही 'क्लाइंटइम्प्ल' उत्पन्न करने के लिए आंतरिक रूप से फैक्ट्री (इसे सिंगलटन बनाना)। इस तरह इंटरफ़ेस नहीं बदलता है, लेकिन क्लाइंट कन्स्ट्रक्टर करता है। –

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

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