2014-11-13 8 views
5

मैं एक एपीआई डिज़ाइन पर काम कर रहा हूं, और जावा और पॉलिमॉर्फिज्म के बारे में कुछ है जो मैंने अभी तक नहीं सोचा था। अगर मैं इस तरह एक API बनाएँ:जावा, पॉलिमॉर्फिज्म, स्टेटिक टाइपिंग और "क्वेरीइंटरफेस" पैटर्न

interface FooFactory 
{ 
    public Foo getFoo(); 
} 

interface Foo 
{ 
    public void sayFoo(); 
} 

तो केवल एक चीज है कि मेरे FooFactory कार्यान्वयन पर भरोसा किया जा सकता है प्रदान करने के लिए एक Foo कार्यान्वयन है। मैं, कुछ बढ़ाया तरीकों प्रदान करने के लिए इस तरह का निर्णय लेते हैं:

interface EnhancedFoo extends Foo 
{ 
    public void patHeadAndRubBelly(); 
} 

class EnhancedFooImpl implements EnhancedFoo 
{ 
    ... implementation here ... 
} 

class EnhancedFooFactoryImpl implements FooFactory 
{ 
    @Override public EnhancedFoo getFoo() { return new EnhancedFooImpl(); } 
} 

और अगर वे एक Foo इंटरफ़ेस प्राप्त करने और EnhancedFoo के रूप में यह कास्ट करने के लिए कोशिश एक ही रास्ता मेरे API क्लाइंट EnhancedFoo इंटरफ़ेस का उपयोग कर सकते हैं।

मैं जिस तरह से माइक्रोसॉफ्ट IUnknown इंटरफ़ेस में COM संभाला याद रखें:

HRESULT QueryInterface(
    [in] REFIID riid, 
    [out] void **ppvObject 
); 

विचार है कि आप इंटरफ़ेस आप चाहते हैं के लिए एक GUID में गुजरती हैं, अगर यह सफल होता है, तो आप वापस एक सूचक आप की गारंटी देता है कि मिलता है आप जिस इंटरफ़ेस को ढूंढ रहे थे उसे सुरक्षित रूप से डाला जा सकता है।

मैं जावा के साथ एक typesafe रास्ते में कुछ इसी तरह कर सकता है:

interface FooFactory 
{ 
    public <T extends Foo> T getFoo(Class<T> fooClass); 
} 

जहाँ मैं अनुरोध है कि या तो वांछित subinterface का एक उदाहरण देता है, या नल लौटाता है यदि कोई भी उपलब्ध है पर एक कार्यान्वयन प्रदान करते हैं।

मेरे सवाल है,:

  • QueryInterface पैटर्न उचित है?
  • क्या मुझे सिर्फ कास्टिंग का उपयोग करना चाहिए?
  • या एपीआई में पॉलिमॉर्फिज्म को संभालने का एकमात्र सही तरीका है ताकि ग्राहकों को सवाल में सादे तरीकों का सख्ती से उपयोग करने से प्रतिबंधित किया जा सके? (उदाहरण के लिए मेरी उदाहरण में Foo में तरीकों और नहीं किसी भी EnhancedFoo तरीकों)

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

मुझे लगता है कि मेरे लिए कठिन हिस्सा यह है कि यहां रनटाइम निर्भरता है ... शुद्ध स्थैतिक प्रकारएफ़ दृष्टिकोण जहां संकलन समय पर सब कुछ तय किया गया है इसका मतलब है कि मैं केवल उस मूल Foo कार्यक्षमता पर निर्भर कर सकता हूं। वह दृष्टिकोण मुझसे परिचित है, लेकिन फिर मैं संभावित उन्नत सुविधाओं पर हार जाता हूं। जबकि अधिक गतिशील/अवसरवादी दृष्टिकोण ऐसा कुछ है जो इन सुविधाओं का लाभ उठा सकता है, लेकिन मुझे इसका उपयोग करने वाली प्रणाली को आर्किटेक्ट करने का सही तरीका नहीं है।

उत्तर

1

आप बस जेनरिक के साथ अपने कारखाने की घोषणा और बाकी को वैसे ही छोड़ सकता है:

static interface FooFactory { 
    public <T extends Foo> T getFoo(); 
} 

तो अनुमान टाइप करने के लिए धन्यवाद, इस संकलन होगा:

FooFactory f = new EnhancedFooFactoryImpl(); 
EnhancedFoo e = f.getFoo(); 

(इस से पहले काम नहीं कर सकता जावा 8)

यदि FooFactory आपकी अपेक्षा नहीं है, तो EnhancedFoo e = f.getFoo();ClassCastException फेंक देगा।

+0

जावा 8 में टाइप अनुमान में सुधार किया गया है। – assylias

+0

हम्म ... पाइथन के लिए अपवाद दृष्टिकोण सामान्य है लेकिन जावा के लिए अनुचित लगता है। –

+0

मैं कहूंगा कि यह पूरी तरह उपयुक्त है - शून्य-जांच – Arkadiy

1

पैरामीट्रिज फैक्टरी इंटरफ़ेस क्यों नहीं?

static interface FooFactory<T extends Foo> { 
    public T getFoo(); 
} 
तो

:

class EnhancedFooFactoryImpl implements FooFactory<EnhancedFoo> { 

    @Override 
    public EnhancedFoo getFoo() { return new EnhancedFooImpl(); } 
} 

और इन्स्टेन्शियशन:

FooFactory<EnhancedFoo> f1 = new EnhancedFooFactoryImpl(); 
EnhancedFoo foo = f1.getFoo(); 

निश्चित रूप से बढ़ाया कारखाने जहां आधार वर्ग की उम्मीद है इस्तेमाल किया जा सकता।

FooFactory<?> f2 = new EnhancedFooFactoryImpl(); 
Foo foo = f2.getFoo(); 

(आपकी टिप्पणी का उत्तर पर) संपादित करें

यदि यह parametrized कारखाने विधि के लिए बेहतर है अपने डिजाइन के लिए नहीं कारखाना वर्ग तो यह एक अच्छा अभ्यास के रूप में नीचे इसे परिभाषित करने की है:

interface FooFactory { 

    public <T extends Foo> T getFoo(Class<T> fooClass); 
} 

इससे आपको दो तरीकों से लाभ मिलता है: 1. आप नियंत्रित कर सकते हैं कि उपयोगकर्ता कौन सा एक्टिव क्लास बनाना चाहता है। 2. कक्षा वस्तु रखने के बाद आप प्रतिबिंब के साथ इसे तुरंत चालू कर सकते हैं। कुछ फू कार्यान्वयन निर्माता आप मानकों की आवश्यकता है

FooFactory ff = new FooFactoryImpl(); 
EnhancedFoo ef = ff.getFoo(EnhancedFoo.class); 
Foo f = ff.getFoo(Foo.class); 

:

class FooFactoryImpl implements FooFactory { 

    @Override 
    public <T extends Foo> T getFoo(Class<T> c) { 
     try { 
      return c.newInstance(); 
     } catch (ReflectiveOperationException e) { 
      return null; 
     } 
    } 
} 

तब उपयोग नीचे की तरह है:

तो इस मामले में आप बढ़ाया foo के लिए विशेष फैक्टरी कक्षाएं करने के लिए की जरूरत नहीं है हमेशा फॉक्टोरी विधि में और ऑब्जेक्ट को "मैन्युअल रूप से" तुरंत चालू कर सकते हैं:

@Override 
    public <T extends Foo> T getFoo(Class<T> c) { 

     if(SomeParametrizedFoo.class.equals(c)) { 
      SomeParamtrizedFoo spf = new SomeParamtrizedFoo("constr arg"); 
      spf.setParam(16136); 
      return (T) spf; 
     } 

     try { 
      return c.newInstance(); 
     } catch (ReflectiveOperationException e) { 
      return null; 
     } 
    } 
+0

से बेहतर मुझे लगता है कि मेरी सोच यह है कि ऐसी विशिष्ट क्षमताएं हैं जिन पर ग्राहक उपयोग करना चाहता है। 'फू' कक्षा में एक से अधिक एक्सटेंशन उपलब्ध हो सकते हैं, इसलिए ऐसा कुछ नहीं है जिसे मैं कारखाने में छोड़ना चाहता हूं; इसके बजाय यह ग्राहक तक होना चाहिए। मैं एपीआई डिज़ाइन पर पर्याप्त अनुभवहीन हूं कि मुझे 100% यकीन नहीं है कि मेरा प्रश्न ठीक से कैसे बनाया जाए। –

+0

ने एक स्पष्टीकरण –

+0

@ जेसनएस ओके जोड़ा, मैंने अपने संपादन में संबोधित किया। –

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