2011-08-06 15 views
9

पर आधारित निर्भरता इंजेक्शन मैं निर्भरता इंजेक्शन के लिए Google Guice का उपयोग कर रहा हूं। मान लीजिए मैं निम्नलिखित है:एक शर्त

public class MyModule extends AbstractModule { 
    @Override 
    protected void configure() { 
     bind(Payment.class).to(PaymentCashImpl.class); 
    } 
} 

आप देख सकते हैं, एक Payment उदाहरण आदेश निर्माता में इंजेक्ट किया जाता:

public interface Payment { 
    public void pay(); 
} 

public class PaymentCardImpl implements Payment { 
    public void pay() { 
     System.out.println("I pay with a card"); 
    } 
} 

public class PaymentCashImpl implements Payment { 
    public void pay() { 
     System.out.println("I pay cash"); 
    } 
} 

public class Order { 

    private Payment payment; 

    @Inject 
    public Order(Payment payment){ 
     this.payment=payment; 
    } 

    public void finishOrder(){ 
     this.payment.pay(); 
    } 
} 

इस से जारी रखते हुए, बंधन, इसलिए की तरह के लिए एक बहुत ही सरल मॉड्यूल है । यह MyModule वर्ग में किया जाता है और समग्र वास्तव में अच्छा है।

मेरा मुख्य दिखेगा:

public static void main(String[] args) { 
    MyModule module = new MyModule(); 
    Injector injector = Guice.createInjector(module); 
    Order order = injector.getInstance(Order.class); 
    order.finishOrder(); 
} 

क्या मैं फिर भी नहीं देख सकते हैं, मैं कैसे किसी तरह सशर्त बाध्य करने के लिए या तो एक PaymentCardImplया एक PaymentCashImplOrder निर्माता के उदाहरण को शामिल कर सकता है।

उदाहरण के लिए कहें कि आदेश 'ऑनलाइन' ऑर्डर था। इसके बाद मुझे इसकी आवश्यकता होगी:

bind(Payment.class).to(PaymentCardImpl.class); 

ऐसा करने का सबसे अच्छा तरीका क्या है? मैं निर्भरता इंजेक्शन के लिए नया हूँ।

+0

जब आप _which_ कार्यान्वयन की जरूरत है पता है? –

उत्तर

5

आप जो भी इंजेक्ट करना चाहते हैं उसे एनोटेट कर सकते हैं। यदि आप नामित बाध्यकारी करते हैं तो यह समस्या को हल करेगा।

नीचे देखें:

bind(Payment.class).annotatedWith(Names.named("Card")).to(PaymentCardImpl.class); 

bind(Payment.class).annotatedWith(Names.named("Cash")).to(PaymentCashImpl.class); 

फिर जहां सुई आप करते हैं:

@Named("Cash") Payment payment 

या:

@Named("Card") Payment payment 
+0

+1 मैं इसे सबसे अच्छे उत्तर के रूप में चिह्नित करने जा रहा हूं क्योंकि यह अंत में उपयोग किया जाने वाला दृष्टिकोण है। यह मुझे सबसे बड़ी लचीलापन की पेशकश की। – Joeblackdev

8

मुझे पता है कि आप ऐसा क्यों करना चाहते हैं। लेकिन मैं व्यापार तर्क के साथ निर्माण कोड (जो निर्भरता इंजेक्शन विन्यास है) मिश्रण नहीं होगा। यदि आप ऐसा करते हैं, तो आपका व्यावसायिक तर्क अब और समझ में नहीं आता है। और मेरे लिए ऐसा लगता है कि आपका सशर्त इंजेक्शन स्थिति पर निर्भर करता है, यानी उपयोगकर्ता इंटरफ़ेस से इनपुट।

तो क्यों न केवल इंजेक्ट करें और स्थिति को स्पष्ट क्यों करें? मैं इसे पसंद करूंगा। एक उदाहरण ऐप:

public class MyModule extends AbstractModule { 
    @Override 
    protected void configure() { 
    } 

    public static void main(String[] args) { 
    MyModule module = new MyModule(); 
    Injector injector = Guice.createInjector(module); 
    Order order = injector.getInstance(Order.class); 
    order.finishOrder(PaymentMethod.CARD); 
    } 
} 

public class PaymentProvider { 
    private final Payment cashPayment, cardPayment; 

    @Inject 
    public PaymentProvider(CardPayment cardPayment, CashPayment cashPayment) { 
    this.cardPayment = cardPayment; 
    this.cashPayment = cashPayment; 
    } 

    public Payment getPaymentByMethod(PaymentMethod method) { 
    switch (method) { 
     case CARD: 
     return cardPayment; 
     case CASH: 
     return cashPayment; 
     default: 
     throw new IllegalArgumentException("Unkown payment method: " + method); 
    } 
    } 
} 

public enum PaymentMethod { CASH, CARD } 

public class Order { 
    private final PaymentProvider paymentProvider; 

    @Inject 
    public Order(PaymentProvider paymentProvider) { 
    this.paymentProvider = paymentProvider; 
    } 

    public void finishOrder(PaymentMethod method) { 
    paymentProvider.getPaymentByMethod(method).pay(); 
    } 
} 

अभी भी अपने अभ्यास के लिए: भुगतान सामग्री। आपको वहां कोई गिइस कोड की आवश्यकता नहीं है। शेष स्वचालित रूप से गुइस द्वारा किया जाता है। यदि आप इंटरफेस का उपयोग करना शुरू करते हैं तो आप यहां वर्णित बाइंडिंग का उपयोग करना शुरू कर देंगे: http://code.google.com/p/google-guice/wiki/GettingStarted। लेकिन अगर आपके पास कोई इंटरफेस नहीं है, तो निर्माण बिना किसी कॉन्फ़िगरेशन के किया जाता है। @ इंजेक्शन एनोटेशन पर्याप्त हैं।

बेशक यह सिर्फ एक उदाहरण डिजाइन है। लेकिन यह दिखाता है कि गुइस के साथ एक अच्छी तरह से decoupled जावा ऐप सेटअप करना कितना आसान है।

+0

आप दोनों इंजेक्ट नहीं कर सकते हैं। अगर मैं कोशिश करता हूं, तो मुझे गिइस से अपवाद मिलता है। मैं केवल एक बाध्यकारी कर सकता हूँ। क्या आप इसका मतलब बता सकते हैं कि आपका क्या मतलब है? मुझे लगता है कि मैं यहां निर्भरता इंजेक्शन का बिंदु खो सकता हूं। मैं रनटाइम बाध्यकारी के बारे में सोच रहा हूँ। – Joeblackdev

+0

यह एक अच्छा अमूर्त नहीं है लेकिन सबसे बेवकूफ तरीका यह होगा (पोस्ट संपादित) –

+0

उदाहरण के लिए धन्यवाद। जब आप कहते हैं कि "इसमें सभी संभावित भुगतान विधियों को इंजेक्ट करें", तो आपका क्या मतलब है? धन्यवाद – Joeblackdev

10

निर्भरता इंजेक्शन service शैली वस्तुओं को बनाने के लिए उपयोगी है। इनमें निम्नलिखित विशेषताएं हैं: -

  • कई कार्यान्वयन की संभावना है,
  • व्यवहार पर भारी,
  • आंतरिक स्थिति उनके निर्भरता तक सीमित है और वे आम तौर पर परिवर्तनशील नहीं
  • में एक अभिनेता के लिए नक्शे होगा वास्तविक दुनिया (उदाहरण के लिए कैशियर),

इस पर आधारित, Payment एक सेवा वस्तु है। मैं इसे PaymentService का नाम बदलकर उस प्रविष्टि से अलग कर सकता हूं, जिसे आप भुगतान के बारे में स्टोर कर सकते हैं (जो एक मूल्य वस्तु होगी)।

उदाहरण के लिए आप Order कक्षा के बारे में बहुत कुछ नहीं दिखाते हैं, लेकिन मुझे लगता है कि यह कुछ लाइन आइटम, डिलीवरी पता और कुल राशि जैसी जानकारी रखेगा। यह value ऑब्जेक्ट है। यह व्यापार डोमेन में एक चीज का प्रतिनिधित्व करता है।

मूल्य वस्तुएं राज्य पर भारी हैं, और व्यवहार पर हल्का है। एकाधिक कार्यान्वयन संभव है, लेकिन आप एक कार्यान्वयन को दूसरे के साथ बदलने की संभावना कम हैं।

मूल्य वस्तुएं आपके निर्भरता इंजेक्शन ढांचे द्वारा नहीं बनाई गई हैं। वे आपके व्यापार तर्क कोड द्वारा बनाए जाते हैं। आपके उदाहरण में, आप गुइस का उपयोग कर सभी ऑब्जेक्ट्स बना रहे हैं। मुझे वास्तविकता की उम्मीद है कि आपको उपयोगकर्ता इनपुट के आधार पर रन-टाइम पर Order बनाना होगा।

सेवा वस्तुएं मूल्य वस्तुओं पर निर्भर हो सकती हैं, लेकिन कभी दूसरी तरफ नहीं। मुझे लगता है कि आप को लागू करने के लिए देख जाना चाहिए:

checkoutService.payfor(order, method);

और नहीं order.finishOrder(method)

CheckoutService वर्ग के भीतर, आप एक approriate PaymentService चुनते हैं और यह करने के लिए order दे सकते हैं। CheckoutService कन्स्ट्रक्टर तर्क PaymentCardPaymentService (आपके PaymentCardImpl के समतुल्य) और CashPaymentService (आपके PaymentCashImpl के बराबर) के रूप में ले जाएगा।

+0

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

+0

वास्तव में एक एसओएपी सेवा क्या है? क्या यह भुगतान कार्यान्वयन है? क्या उनके पास यूआरएल, प्रॉक्सी कॉन्फ़िगरेशन इत्यादि जैसे कुछ राज्य नहीं होंगे? मुझे गुइस नहीं पता है इसलिए मैं एक उदाहरण नहीं दे सकता लेकिन मुझे लगता है कि आपको अपनी कक्षाएं सीधे पहले प्राप्त करने की आवश्यकता है और फिर अन्य उत्तरदाताओं के उत्तर उपयोगी हैं। –

3

MapBinder एक्सटेंशन का उपयोग करके आप Map में निहित कई बाइंडिंग के साथ इंजेक्शन प्राप्त कर सकते हैं। फिर यह कार्यान्वयन का विषय है जिसका उपयोग करने के लिए एक है।

आप अपने मामले में एक महत्वपूर्ण, कि हो सकता है एक String या Enumeration और उचित चाबियाँ करने के लिए सभी Payment कार्यान्वयन बाँध की आवश्यकता होगी:

public class MyModule extends AbstractModule { 
    @Override 
    protected void configure() { 
    // this one aggregates all bindings and could be further annotated if you 
    // need several maps with same key/value types 
    MapBinder<String, Payment> mapBinder = MapBinder.newMapBinder(binder(), 
     String.class, Payment.class); 

    // simple binding of PaymentCashImpl to 'cash' key 
    mapBinder.addBinding("cash").to(PaymentCashImpl.class); 

    // you can scope during binding or using @Singleton annotation on implementations 
    mapBinder.addBinding("card").to(PaymentCardImpl.class).in(Singleton.class); 
    } 
} 
फिर Order में

आप पूरे नक्शे के साथ इंजेक्शन हो और तय करते हैं जो कार्यान्वयन उपयोग करने के लिए:

public class Order { 
    @Inject 
    Map<String, Provider<Payment>> paymentProcessors; 

    public void finishOrder(String paymentMethod) { 
    if (!paymentProcessors.containsKey(paymentMethod)) { 
     throw new IllegalArgumentException("Unknown payment method " + paymentMethod); 
    } 
    Payment paymentProcessor = paymentProcessors.get(paymentMethod).get(); 

    // do your stuff... 
    paymentProcessor.pay(); 
    } 
} 

नोट: इंजेक्शन प्रदाताओं javax.inject.Provider साथ काम नहीं करता है, तो आप की जरूरत है com.google.inject.Provider का उपयोग करने के लिए।

उदाहरणों के बजाय प्रदाताओं को इंजेक्शन करके आप आलसी तत्कालता और कार्यान्वयन के लिए विभिन्न तत्काल नीतियां प्राप्त कर सकते हैं। PaymentCardImpl के ऊपर दिए गए उदाहरण की तरह सिंगलटन है, जबकि दूसरा हर बार बनाया जाता है। आप जीवनकाल को नियंत्रित करने के लिए GUICE scopes का उपयोग कर सकते हैं, या किसी अन्य चीज़ को पूरा करने के लिए अपने स्वयं के प्रदाता को भी कार्यान्वित कर सकते हैं।

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