2015-04-28 8 views
16

उदाहरण के लिए, अगर मैं इतने सारे वर्ग एक ही मंच में एक ही उपसर्ग है: एंड्रॉयड मेंवर्ग नाम के किसी हिस्से को मैक्रो के रूप में परिभाषित करने के लिए कैसे करें?

:

Printer *p=new AndroidPrinter(); 
Writer *w=new AndroidWriter(); 
Connector *c=new AndroidConnector(); 
आईओएस में

:

Printer *p=new IOSPrinter(); 
Writer *w=new IOSWriter(); 
Connector *c=new IOSConnector(); 

एक हिस्से को परिभाषित करने के लिए यह संभव है वर्ग के नाम की तरह:

#define PREFIX Android 

int main(){ 
    Printer *p=new PREFIXPrinter(); 
    Writer *w=new PREFIXWriter(); 
    Connector *c=new PREFIXConnector(); 
    return 0; 
} 

के बजाय:

#define PLATFORM 0 

#if PLATFORM==0 
#define PRINTER AndroidPrinter 
#else 
#define PRINTER IOSPrinter 
#endif 

#if PLATFORM==0 
#define WRITER AndroidWriter 
#else 
#define WRITER IOSWriter 
#endif 

#if PLATFORM==0 
#define CONNECTOR AndroidConnector 
#else 
#define CONNECTOR IOSConnector 
#endif 

int main(){ 
    Printer *p=new PRINTER(); 
    Writer *w=new WRITER(); 
    Connector *c=new CONNECTOR(); 
    return 0; 
} 
+19

क्या आप इसके बजाय नेमस्पेस का उपयोग नहीं कर सकते? जैसे 'एंड्रॉइड :: प्रिंटर', 'एंड्रॉइड :: राइटर', आदि – Biffen

+4

नेमस्पेस सबसे अच्छा समाधान प्रतीत होता है (प्रीप्रोसेसर ब्लैक मैजिक से काफी बेहतर)। यह पढ़ने के लिए बस अधिक रखरखाव/आसान है। –

उत्तर

17

आप इसके लिए कुछ टेम्पलेट विशेषज्ञता के साथ एक स्थिर कारखाने पैटर्न का उपयोग कर सकते हैं। आपको वास्तव में पूरी तरह से अमूर्त कारखाने की आवश्यकता नहीं है, क्योंकि आप निष्पादन के माध्यम से आधे रास्ते पर स्विचिंग नहीं कर पाएंगे। इस तरह, आप बिना किसी ओवरहेड के संकलन-समय पर उपयोग करने के लिए कौन सा फैक्ट्री कार्यान्वयन कर सकते हैं (यह मानते हुए कि आपके कारखाने के तरीके इनलाइन हैं और बस operator new पर आगे बढ़ें)।

प्लेटफ़ॉर्म पहचानकर्ता के रूप में कार्य करने के लिए डमी structs के एक जोड़े को परिभाषित करें:

struct Android{}; 
struct IOS{}; 

कुछ कारखाने कार्यान्वयन लिखें उन डमी structs के आधार पर:

template <typename T> 
struct ConcreteComponentFactory; 

template <> 
struct ConcreteComponentFactory<Android> 
{ 
    static Printer *CreatePrinter() 
    { return new AndroidPrinter(); } 
    static Writer *CreateWriter() 
    { return new AndroidWriter(); } 
    static Connector *CreateConnector() 
    { return new AndroidConnector(); } 
}; 

template <> 
struct ConcreteComponentFactory<IOS> 
{ 
    static Printer *CreatePrinter() 
    { return new IOSPrinter(); } 
    static Writer *CreateWriter() 
    { return new IOSWriter(); } 
    static Connector *CreateConnector() 
    { return new IOSConnector(); } 
}; 

क्या मंच कुछ मैक्रो के आधार पर उपयोग करने के लिए व्यायाम करें :

#define PLATFORM 0 
#if PLATFORM==0 
using Platform = Android; 
#else 
using Platform = IOS; 
#endif 

फिर प्लेटफ़ॉर्म के लिए उदाहरण टाइप करें पर काम कर रहा:

using ComponentFactory = ConcreteComponentFactory<Platform>; 

अब हम हमारी वस्तुओं के उदाहरण बना सकते हैं और हम क्या मंच पर हैं के बारे में भूल कर सकते हैं:

Writer *a = ComponentFactory::CreateWriter(); 

हम पता है कि मंच हम कुछ बिंदु पर कर रहे हैं की जरूरत है, हम कर सकते हैं बस Platform टाइपपीफ देखें।

यह दृष्टिकोण आपके पास मैक्रो-आधारित संस्करण की तुलना में काफी लचीला और कहीं अधिक प्रकार-सुरक्षित है।

Demo

+0

मुझे यह पसंद है क्योंकि सबकुछ संकलन समय पर है। – refi64

8

आप नहीं कर सकते। PREFIXPrinter(); एक समारोह PREFIXPrinter(); नाम नहीं AndroidPrinter();

आप इसे इस तरह से कर सकते हैं हो जाएगा:

#define PREFIX(a) Android##a 

उपयोग:

PREFIX(Printer)(); 

यह फोन करेगा AndroidPrinter()

11

आप वास्तव में नहीं हो सकता वाक्यविन्यास आप चाहते हैं। हालांकि, अगर आप कुछ और पूर्वप्रक्रमक जादू कर सकते हैं कि यह काम करने के लिए:

#define CONCAT_HELPER(a,b) a ## b 
#define CONCAT(a,b) CONCAT_HELPER(a,b) 

#define x ABC 

#define MAKENAME(y) CONCAT(x,y) 

int MAKENAME(def); // this defines ABCdef variable 

int main() { 
    ABCdef = 0; 
    return 0; 
} 

हालांकि, यह एक और दृष्टिकोण का उपयोग करने के लिए एक बेहतर तरीका है, इस तरह के रूप नाम स्थान टिप्पणी में सुझाव दिया है, या और भी बेहतर एक सार कारखाने, कुछ होगा इस तरह:

class Factory { 
public: 
    virtual Printer* getPrinter() = 0; 
    virtual Writer* getWriter() = 0; 
}; 
class AndroidFactory: public Factory { 
public: 
    virtual Printer* getPrinter() { return new AndroidPrinter(); } 
    virtual Writer* getWriter() { return new AndroidWriter(); } 
}; 
// the same for IOS, or, 
// if you really have such a similar code here, 
// you can make a macros to define a factory 

... 
int main() { 
    #ifdef ANDROID 
     Factory* factory = new AndroidFactory(); 
    #else 
     Factory* factory = new IOSFactory(); 
    #endif 
    // now just pass factory pointer everywhere 
    doSomething(old, parameters, factory); 
} 

(और भी बेहतर auto_ptr रों या यहाँ तक कि unique_ptr रों उपयोग करने के लिए किया जाएगा।)

(आप भी अगर आप चाहते हैं अपने कारखाने एक सिंगलटन कर सकते हैं।)

अद्यतन:

एक और दृष्टिकोण दो स्वतंत्र कारखाना वर्गों के लिए और सिर्फ typedef उनमें से एक का उपयोग करने के लिए है:

class AndroidFactory { // no inheritance here 
    public: 
     Printer* getPrinter() {...} // no virtual here! 
     ... 
}; 
class IOSFactory { 
    ... 
}; 
#ifdef ANDROID 
typedef AndroidFactory Factory; 
#else 
typedef IOSFactory Factory; 
#endif 

// note that we pass Factory* here 
// so it will compile and work on both Android and IOS 
void doSomething(int param, Factory* factory); 

int main() { 
    Factory* factory = new Factory(); 
    // and pass factory pointer around 
    doSomething(param, factory); 
} 

देखें इस दृष्टिकोण का एक और अधिक उन्नत संस्करण के लिए भी TartanLlama का जवाब, सहित चारों ओर एक पॉइंटर पास करने से बचने के लिए स्थिर कार्य।

इस दृष्टिकोण का एक लाभ है कि संकलन समय पर सबकुछ हल हो गया है और कोई वर्चुअल कॉल नहीं किया गया है।

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

Foo* foo = factory.getFoo(); 
foo->doSomething(); 

और ज्यादातर समय doSomething() कॉल में खर्च करते हैं, तो getFoo() आभासी कॉल भूमि के ऊपर एक टोंटी नहीं होगा किया जाएगा की तर्ज पर कुछ हो जाएगा।

साथ ही, इस दृष्टिकोण के पास एक नुकसान है कि इसमें उचित विरासत संरचना की कमी है और इस प्रकार विस्तार को और अधिक कठिन बनाता है (और स्थिर कार्य विस्तार को और भी कठिन बना देंगे)।

कल्पना कीजिए कि आप एंड्रॉइड फोन और एंड्रॉइड टैबलेट के लिए कुछ अलग कारखानों को रखना चाहते हैं। विरासत के साथ, आपके पास बस एक मूल AndroidBaseFactory कक्षा होगी जिसमें सामान्य तर्क और फोन और टैबलेट के लिए दो उप-वर्ग होंगे जो आपको चाहिए। विरासत के बिना आपको फैक्ट्री कक्षाओं में सभी सामान्य कार्यक्षमताओं की प्रतिलिपि बनाना होगा (भले ही यह केवल return new AndroidFoo(); कॉल हो)।

इसके अलावा, यूनिट परीक्षण और मॉकिंग विरासत और पॉइंटर्स के आसपास गुजरने के साथ आसान है।

वास्तव में, "सिंगलटन बनाम स्थिर कार्यों का एक गुच्छा" के कई तर्क यहां भी लागू होते हैं।

+1

ऑटोपटर का उपयोग न करें, unique_ptrs – paulm

+0

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

+0

@ kirbyfan64sos, मैंने जवाब को अद्यतन किया है, विशेष रूप से, चर्चा क्यों मुझे लगता है कि वास्तविक कारखाने के नुकसान से अधिक फायदे हैं। – Petr

1

यह संभव नहीं है, लेकिन आप समान सामान्यीकरण प्रभाव से अन्य बातों के कर सकते हैं।

  1. @Biffen के रूप में बताया, तो आप namespaces

  2. उपयोग कर सकते हैं @Petr के रूप में बताया, तो आप एक abstract factory

  3. उपयोग कर सकते हैं के रूप में @Petr अपने जवाब में बताया, तो आप क्या कर सकते हैं कुछ पूर्वप्रक्रमक जादू

  4. आप उपयोग कर सकते हैं reflection

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

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