2013-04-08 5 views
5

मुझे बहुत सारे श्रोताओं मिल गए हैं जो addListener के बजाय setListener विधियों का उपयोग करके पंजीकृत हैं। तो एकाधिक श्रोताओं को किसी ऑब्जेक्ट में पंजीकरण करने की अनुमति देने के लिए, मुझे मल्टीप्लेक्सर्स का उपयोग करना होगा। यह ठीक है, लेकिन अब मुझे अपने श्रोता इंटरफ़ेस के लिए एक मल्टीप्लेक्सर बनाना है। तो मेरा सवाल है: क्या निम्नलिखित कोड के लिए आवश्यक Mux.create() को कार्यान्वित करना संभव है?जावा में एक सामान्य उद्देश्य मल्टीप्लेक्सर लिखना संभव है?

AppleListener appleListener1 = new AppleProcessorA(); 
AppleListener appleListener2 = new AppleProcessorB(); 
AppleListener appleListenerMux = Mux.create(appleListener1, appleListener2); 
Apple apple = new Apple(); 
apple.setListener(appleListenerMux); 

OrangeListener orangeListener1 = new OrangeProcessorA(); 
OrangeListener orangeListener2 = new OrangeProcessorB(); 
OrangeListener orangeListenerMux = Mux.create(orangeListener1, orangeListener2); 
Orange apple = new Orange(); 
orange.setListener(orangeListenerMux); 

class Mux { 
    public static <T> T create(T... outputs) { } 
} 

मुझे लगता है कि प्रतिबिंब का उपयोग करके यह संभव हो सकता है। क्या प्रतिबिंब का उपयोग करने का कोई कारण बुरा विचार होगा? (प्रदर्शन दिमाग में आता है)

+0

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

+0

क्या आप अपने मल्टीप्लेक्सर के बारे में अपने दिमाग में क्या है इसके बारे में अधिक समझा सकते हैं? क्योंकि एलिस्टेयर इज़राइल ने कहा था कि वही बात मेरे दिमाग में आई थी। –

+2

जबकि यह आपके लिए लक्षित है, इसके अलावा वे एक [प्रॉक्सी] (http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html) कक्षा का उपयोग कर सकते हैं। यह एक ऑब्जेक्ट बनाता है जो दिए गए वर्ग की तरह दिखता है लेकिन इसके कॉल को संसाधित करने के लिए हैंडलर को कॉल करता है, हैंडलर श्रोताओं के माध्यम से फिर से चला सकता है। – BevynQ

उत्तर

9

गतिशील Proxy का उपयोग करना संभव है।

सबसे आसान तरीका वांछित इंटरफ़ेस में भी आपके Mux.create() कॉल के पहले पैरामीटर के रूप में पास करना है। अन्यथा, आपको प्रदान किए गए सभी कंक्रीट श्रोता उदाहरणों से वांछित इंटरफ़ेस का अनुमान लगाने के लिए प्रतिबिंब का उपयोग करना होगा (यह निर्धारित करना मुश्किल है कि सभी श्रोता ऑब्जेक्ट्स सामान्य रूप से कई इंटरफेस लागू करते हैं)। इस प्रकार,

public class Mux { 

    /** 
    * @param targetInterface 
    *   the interface to create a proxy for 
    * @param instances 
    *   the concrete instances to delegate to 
    * @return a proxy that'll delegate to all the arguments 
    */ 
    @SuppressWarnings("unchecked") 
    public static <T> T create(Class<T> targetInterface, final T... instances) { 
     ClassLoader classLoader = targetInterface.getClassLoader(); 
     InvocationHandler handler = new InvocationHandler() { 
      @Override 
      public Object invoke(Object proxy, Method m, Object[] args) 
        throws Throwable { 
       for (T instance : instances) { 
        m.invoke(instance, args); 
       } 
       return null; 
      } 
     }; 
     return (T) Proxy.newProxyInstance(classLoader, 
       new Class<?>[] { targetInterface }, handler); 
    } 
} 

जो तुम का प्रयोग करेंगे, उदाहरण के लिए:

Apple apple = new Apple(); 
AppleListener l1 = new AppleListenerA(); 
AppleListener l2 = new AppleListenerB(); 
apple.setListener(Mux.create(AppleListener.class, l1, l2)); 
apple.doSomething(); // will notify all listeners 

यह केवल एक गतिशील Proxy कि लक्ष्य प्रकार के लिए डाला जाता है बनाकर काम करता है

यहाँ यह की कमी है T। वह प्रॉक्सी InvocationHandler का उपयोग करती है जो कि कंक्रीट उदाहरणों को प्रॉक्सी में केवल सभी विधि कॉल का प्रतिनिधित्व करती है।

ध्यान दें कि जबकि सामान्य रूप में मैं सभी मापदंडों और स्थानीय चर जहां भी संभव को अंतिम रूप देने, मैं केवल T... instances इस मामले में अंतिम रूप दे दिया तथ्य यह है कि अगर instances था उजागर करने के लिए नहीं अंतिम है, तो एक गुमनाम आंतरिक वर्ग के भीतर यह संदर्भित नहीं होगा अनुमति दी जायेगी (आपको एक अलग विधि में परिभाषित आंतरिक कक्षा के अंदर एक गैर-अंतिम परिवर्तनीय तर्कों का संदर्भ नहीं मिल सकता है ")।

यह भी ध्यान दें कि उपरोक्त मानते हैं कि वास्तविक विधि कॉल किसी भी सार्थक (या उपयोगी) मान वापस नहीं लौटाती है, इसलिए handler सभी विधि कॉल के लिए null भी लौटाता है। यदि आप रिटर्न वैल्यू एकत्र करना चाहते हैं और उन्हें सार्थक तरीके से वापस करना चाहते हैं तो आपको थोड़ा और कोड जोड़ना होगा।


वैकल्पिक रूप से, एक सब दिया instances आम इंटरफेस वे सभी को लागू निर्धारित करने के लिए निरीक्षण किया है, और सभी newProxyInstance() करने के लिए उन पारित कर सकते हैं। यह Mux.create() को अपने व्यवहार पर कुछ नियंत्रण के नुकसान पर उपयोग करने के लिए बहुत सुविधाजनक बनाता है।

/** 
* @param instances 
*   the arguments 
* @return a proxy that'll delegate to all the arguments 
*/ 
@SuppressWarnings("unchecked") 
public static <T> T create(final T... instances) { 

    // Inspect common interfaces 
    final Set<Class<?>> commonInterfaces = new HashSet<Class<?>>(); 
    commonInterfaces.addAll(Arrays.asList(instances[0].getClass() 
      .getInterfaces())); 

    // Or skip instances[0] 
    for (final T instance : instances) { 
     commonInterfaces.retainAll(Arrays.asList(instance.getClass() 
       .getInterfaces())); 
    } 

    // Or use ClassLoader.getSystemClassLoader(); 
    final ClassLoader classLoader = instances[0].getClass().getClassLoader(); 

    // magic 
    final InvocationHandler handler = new InvocationHandler() { 
     @Override 
     public Object invoke(final Object proxy, final Method m, final Object[] args) 
       throws Throwable { 
      for (final T instance : instances) { 
       m.invoke(instance, args); 
      } 
      return null; 
     } 
    }; 

    final Class<?>[] targetInterfaces = commonInterfaces 
      .toArray(new Class<?>[commonInterfaces.size()]); 
    return (T) Proxy.newProxyInstance(classLoader, targetInterfaces, 
      handler); 
} 
+0

धन्यवाद, मैं आपके उत्तर की पूर्णता की सराहना करता हूं। जिज्ञासा से एक प्रश्न: क्या हम इसे सामान्य तर्क 'टी' से इंटरफ़ेस का अनुमान लगा सकते हैं, इसे एक और तर्क के रूप में पारित करने के बजाय? –

+0

@DylanP आप सामान्य पैरामीटर 'टी' से कुछ भी अनुमान नहीं लगा सकते हैं, क्योंकि यह 'संशोधित' नहीं है-यानी, पैरामीटर रन-टाइम पर वास्तव में उपलब्ध नहीं है। _are_, हालांकि, कुछ किनारे के मामले या चालें जो रन-टाइम पर जेनेरिक प्रकार की जानकारी का अनुमान लगाना संभव बनाती हैं: [subclasses] में (http://alistairisrael.wordpress.com/2009/05/28/introducing-magictest/) , और [प्रकार टोकन] का उपयोग कर (http://www.jquantlib.org/index.php/Using_TypeTokens_to_retrieve_generic_parameters)। –

+0

@DylanP आपके मामले में, हालांकि, मुझे लगता है कि पारित ठोस उदाहरणों से वांछित इंटरफ़ेस को आज़माकर देखना आसान होगा। बस उन सभी का निरीक्षण करें और उन सभी सामान्य इंटरफ़ेस को ढूंढें जिन्हें वे सभी कार्यान्वित करते हैं, फिर उन सभी को 'प्रॉक्सी.न्यूप्रोक्सी इंस्टेंस()' कॉल पर पास करें। सबसे पहले मुझे एहसास नहीं हुआ कि 'newProxyInstance()' ने कई इंटरफेस स्वीकार किए हैं। मुझे इस अतिरिक्त तकनीक के साथ अपना जवाब अपडेट करने दें। –

0

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

AppleListener appleListener1 = new AppleProcessorA(); 
AppleListener appleListener2 = new AppleProcessorB(); 
CompositeListener composite = CompositeListener.for(appleListener1, appleListener2); 
Apple apple = new Apple(); 
apple.setListener(composite); 

आप AppleListener और OrangeListener refactor करने के लिए एक श्रोता इंटरफ़ेस है कि इस विषय श्रोताओं को सूचित करने के लिए एक विधि शामिल लागू करने के लिए हो सकता है। समग्र लिस्टनर को समग्र पैटर्न को लागू करने के लिए इस श्रोता को भी विस्तारित करना होगा।

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