सार्वजनिक एपीआई डिजाइन के साथ निर्भरता इंजेक्शन संतुलन


13

मैं इस बात पर विचार कर रहा हूं कि सरल फिक्स्ड पब्लिक एपीआई प्रदान करने के साथ निर्भरता इंजेक्शन का उपयोग करके परीक्षण योग्य डिजाइन को कैसे संतुलित किया जाए। मेरी दुविधा यह है: लोग कुछ करना चाहते हैं var server = new Server(){ ... }और कई निर्भरता और निर्भरता के ग्राफ बनाने के बारे में चिंता करने की ज़रूरत नहीं Server(,,,,,,)है। विकसित होने के दौरान, मैं बहुत ज्यादा चिंता नहीं करता, क्योंकि मैं सभी को संभालने के लिए एक IoC / DI ढांचे का उपयोग करता हूं (मैं किसी भी कंटेनर के जीवनचक्र प्रबंधन पहलुओं का उपयोग नहीं कर रहा हूं, जो आगे चीजों को जटिल करेगा)।

अब, निर्भरताएँ फिर से लागू होने की संभावना नहीं है। विस्तार के लिए सीम बनाने के बजाय परीक्षण (और सभ्य डिजाइन!) के लिए इस मामले में घटक लगभग विशुद्ध रूप से है, लोग 99.999% समय एक डिफ़ॉल्ट कॉन्फ़िगरेशन का उपयोग करना चाहते हैं। इसलिए। मैं निर्भरता को हार्डकोड कर सकता था। हम ऐसा नहीं करना चाहते, हम अपना परीक्षण खो देते हैं! मैं हार्ड-कोडित निर्भरता के साथ एक डिफ़ॉल्ट कंस्ट्रक्टर प्रदान कर सकता था और जो निर्भरता लेता है। यह ... गड़बड़ है, और भ्रमित होने की संभावना है, लेकिन व्यवहार्य है। मैं कंस्ट्रक्टर प्राप्त करने वाले पर निर्भरता को आंतरिक बना सकता था और अपनी इकाई परीक्षणों को एक दोस्त असेंबली (सी # मानकर) कर सकता था, जो सार्वजनिक एपीआई को टिडिज करता है, लेकिन रखरखाव के लिए एक बुरा छिपे हुए जाल को छोड़ देता है। दो रचनाकारों के होने के कारण जो स्पष्ट रूप से स्पष्ट रूप से जुड़े हुए हैं मेरी पुस्तक में सामान्य रूप से खराब डिजाइन होंगे।

फिलहाल मैं कम से कम बुराई के बारे में सोच सकता हूं। राय? बुद्धिमत्ता?

जवाबों:


11

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

पहले विचार करें कि आप किसी से अपने एपीआई का उपयोग करने की उम्मीद कैसे करेंगे - इसमें शामिल रूपरेखा के बिना।

  • आप सर्वर को तुरंत कैसे करते हैं?
  • आप सर्वर का विस्तार कैसे करते हैं?
  • बाहरी एपीआई में आप क्या उजागर करना चाहते हैं?
  • आप बाहरी एपीआई में क्या छिपाना चाहते हैं ?

मुझे आपके घटकों के लिए डिफ़ॉल्ट कार्यान्वयन प्रदान करने का विचार पसंद है, और यह तथ्य कि आपके पास वे घटक हैं। निम्नलिखित दृष्टिकोण एक पूरी तरह से मान्य और सभ्य OO अभ्यास है:

public Server() 
    : this(new HttpListener(80), new HttpResponder())
{}

public Server(Listener listener, Responder responder)
{
    // ...
}

जब तक आप दस्तावेज करते हैं कि एपीआई डॉक्स में घटकों के लिए डिफ़ॉल्ट कार्यान्वयन क्या है, और एक निर्माता प्रदान करता है जो सभी सेटअप करता है, तो आपको ठीक होना चाहिए। जब तक सेट कोड एक मास्टर कंस्ट्रक्टर में नहीं होता तब तक कैस्केडिंग निर्माणकर्ता नाजुक नहीं होते हैं।

अनिवार्य रूप से, सभी सार्वजनिक रूप से सामना करने वाले निर्माणकर्ताओं के पास उन घटकों के विभिन्न संयोजन होंगे जिन्हें आप उजागर करना चाहते हैं। आंतरिक रूप से, वे चूक को रोकेंगे और सेटअप पूरा करने वाले निजी कंस्ट्रक्टर को टाल देंगे। संक्षेप में, यह आपको कुछ DRY प्रदान करता है और परीक्षण करने में बहुत आसान है।

यदि आपको friendपरीक्षण के लिए इसे अलग करने के लिए सर्वर ऑब्जेक्ट सेट करने के लिए अपने परीक्षणों तक पहुंच प्रदान करने की आवश्यकता है , तो इसके लिए जाएं। यह सार्वजनिक API का हिस्सा नहीं होगा, जिसे आप सावधान करना चाहते हैं।

बस अपने एपीआई उपभोक्ताओं की तरह रहें और अपने पुस्तकालय का उपयोग करने के लिए आईओसी / डीआई ढांचे की आवश्यकता नहीं है । दर्द के लिए आप महसूस कर रहे हैं कि यह सुनिश्चित करने के लिए कि आपके यूनिट परीक्षण IoC / DI फ्रेमवर्क पर भरोसा नहीं करते हैं। (यह एक व्यक्तिगत पालतू पशु है, लेकिन जैसे ही आप रूपरेखा पेश करते हैं, यह अब इकाई परीक्षण नहीं है - यह एकीकरण परीक्षण बन जाता है)।


मैं सहमत हूं, और मैं निश्चित रूप से IoC कॉन्फिग सूप का प्रशंसक नहीं हूं - XML ​​कॉन्फ़िगरेशन कुछ ऐसा नहीं है जिसका मैं उपयोग करता हूं जहां IoC का संबंध है। निश्चित रूप से आईओसी के उपयोग की आवश्यकता वही है जो मैं टाल रहा था - यह एक तरह की धारणा है जिसे सार्वजनिक एपीआई डिजाइनर कभी नहीं बना सकता है। टिप्पणी के लिए धन्यवाद, यह उन पंक्तियों के साथ है जो मैं सोच रहा था और यह अब और फिर से एक पवित्रता की जांच करने के लिए आश्वस्त है!
कोलेक्टिव

-1 यह उत्तर मूल रूप से कहता है "निर्भरता इंजेक्शन का उपयोग न करें" और इसमें गलत धारणाएं हैं। आपके उदाहरण में अगर HttpListenerकंस्ट्रक्टर बदलता है तो Serverक्लास को भी बदलना होगा। वे कपल हैं। एक बेहतर समाधान है, जो नीचे @ आइंस्टीन इवर्ट कहते हैं, जो कि पुस्तकालय के एपीआई के बाहर, पूर्वनिर्धारितताओं के साथ एक डिफ़ॉल्ट वर्ग प्रदान करना है। तब प्रोग्रामर को आपके लिए निर्णय लेने के बाद से सभी निर्भरताएं स्थापित करने के लिए मजबूर नहीं किया जाता है, लेकिन उसके पास अभी भी बाद में बदलने की लचीलापन है। जब आप तृतीय-पक्ष निर्भरता रखते हैं तो यह एक बड़ी बात है।
एम। डडले

एफडब्ल्यूआईडब्ल्यू प्रदान उदाहरण को "कमीने इंजेक्शन" कहा जाता है। stackoverflow.com/q/2045904/111327 stackoverflow.com/q/6733667/111327
एम। डडले

1
@MichaelDudley, क्या आपने ओपी के प्रश्न को पढ़ा है। सभी "धारणाएं" ओपी की प्रदान की गई जानकारी पर आधारित थीं। एक समय के लिए, "कमीने इंजेक्शन" जैसा कि आपने इसे कहा था, पसंदीदा निर्भरता इंजेक्शन प्रारूप था - विशेष रूप से उन प्रणालियों के लिए जिनकी रचनाएँ रनटाइम के दौरान नहीं बदलती हैं। सेटर्स और गेटर्स भी निर्भरता इंजेक्शन का दूसरा रूप हैं। मैंने ओपी को जो प्रदान किया है उसके आधार पर मैंने केवल उदाहरणों का उपयोग किया है।
बेरिन लोरिट्श

4

जावा में ऐसे मामलों में फैक्ट्री द्वारा निर्मित "डिफ़ॉल्ट" कॉन्फ़िगरेशन होना सामान्य है, वास्तविक "ठीक से" व्यवहार करने वाले सर्वर से स्वतंत्र रखा जाता है। तो, आपका उपयोगकर्ता लिखता है:

var server = DefaultServer.create();

जबकि Serverनिर्माणकर्ता अभी भी अपनी सभी निर्भरताओं को स्वीकार करता है और गहरे अनुकूलन के लिए इस्तेमाल किया जा सकता है।


+1, एसआरपी। दंत चिकित्सक कार्यालय की जिम्मेदारी दंत चिकित्सक के कार्यालय का निर्माण करना नहीं है। सर्वर की जिम्मेदारी सर्वर बनाने की नहीं है।
आर। श्मिट

2

कैसे उपवर्ग प्रदान करने के बारे में जो "सार्वजनिक आपी" प्रदान करता है

class StandardServer : Server
{
    public StandardServer():
        this( depends1, depends2, depends3)
    {
    }
}

उपयोगकर्ता new StandardServer()अपने रास्ते पर हो सकता है। वे सर्वर बेस क्लास का भी उपयोग कर सकते हैं यदि वे सर्वर पर अधिक नियंत्रण चाहते हैं। यह मेरे द्वारा उपयोग किए जाने वाले दृष्टिकोण से अधिक या कम है। (मैं एक रूपरेखा का उपयोग नहीं करता हूं क्योंकि मैंने अभी तक बिंदु नहीं देखा है।)

यह अभी भी आंतरिक एपीआई को उजागर करता है, लेकिन मुझे लगता है कि आपको चाहिए। आपने अपनी वस्तुओं को विभिन्न उपयोगी घटकों में विभाजित किया है जो स्वतंत्र रूप से काम करना चाहिए। जब कोई तीसरा पक्ष अलगाव में घटकों में से एक का उपयोग करना चाहता हो सकता है, तो कोई बताने वाला नहीं है।


1

मैं कंस्ट्रक्टर प्राप्त करने वाले पर निर्भरता को आंतरिक बना सकता था और अपनी इकाई परीक्षणों को एक दोस्त असेंबली (सी # मानकर) कर सकता था, जो सार्वजनिक एपीआई को टिडिज करता है, लेकिन रखरखाव के लिए एक बुरा छिपे हुए जाल को छोड़ देता है।

यह मेरे लिए एक "बुरा छिपा हुआ जाल" जैसा प्रतीत नहीं होता है। सार्वजनिक निर्माणकर्ता को केवल "डिफ़ॉल्ट" निर्भरता वाले आंतरिक को कॉल करना चाहिए। जब तक यह स्पष्ट है कि सार्वजनिक निर्माण को संशोधित नहीं किया जाना चाहिए, तब तक सब कुछ ठीक होना चाहिए।


0

मैं आपकी राय से पूरी तरह सहमत हूं। हम इकाई परीक्षण के उद्देश्य से घटक उपयोग सीमा को प्रदूषित नहीं करना चाहते हैं। यह डीआई आधारित परीक्षण समाधान की पूरी समस्या है।

मैं InjectableFactory / InjectableInstance पैटर्न का उपयोग करने का सुझाव दूंगाInjectableInstance एक साधारण पुन: प्रयोज्य उपयोगिता वर्ग हैं जो कि उत्परिवर्तनीय मूल्य धारक हैं । घटक इंटरफ़ेस में इस मान धारक का संदर्भ होता है जो डिफ़ॉल्ट कार्यान्वयन के साथ प्रारंभ होता है। इंटरफ़ेस एक सिंगलटन विधि प्रदान करेगा () जो मूल्य धारक को सौंपता है। घटक ग्राहक प्रत्यक्ष निर्भरता से बचने के लिए कार्यान्वयन का एक उदाहरण प्राप्त करने के लिए नए के बजाय प्राप्त () विधि को कॉल करेगा । परीक्षण के समय के दौरान, हम डिफ़ॉल्ट कार्यान्वयन को वैकल्पिक रूप से एक नकली के साथ बदल सकते हैं। निम्नलिखित मैं एक उदाहरण TimeProvider का उपयोग करूंगावर्ग जो तारीख का एक अमूर्त है () तो यह इकाई परीक्षण को अलग-अलग पल का अनुकरण करने के लिए इंजेक्शन और नकली करने की अनुमति देता है। क्षमा करें, मैं यहाँ जावा का उपयोग करूँगा क्योंकि मैं जावा से अधिक परिचित हूँ। C # समान होना चाहिए।

public interface TimeProvider {
  // A mutable value holder with default implementation
  InjectableInstance<TimeProvider> instance = InjectableInstance.of(Impl.class);  
  static TimeProvider get() { return instance.get(); }  // Singleton method.

  class Impl implements TimeProvider {        // Default implementation                                    
    @Override public Date getDate() { return new Date(); }
    @Override public long getTimeMillis() { return System.currentTimeMillis(); }
  }

  class Mock implements TimeProvider {   // Mock implemention
    @Setter @Getter long timeMillis = System.currentTimeMillis();
    @Override public Date getDate() { return new Date(timeMillis); }
    public void add(long offset) { timeMillis += offset; }
  }

  Date getDate();
  long getTimeMillis();
}

// The client of TimeProvider
Order order = new Order().setCreationDate(TimeProvider.get().getDate()));

// In the unit testing
TimeProvider.Mock timeMock = new TimeProvider.Mock();
TimeProvider.instance.setInstance(timeMock);  // Inject mock implementation

InjectableInstance को लागू करना बहुत आसान है, मेरे पास जावा में एक संदर्भ कार्यान्वयन है। विवरण के लिए, कृपया मेरे ब्लॉग पोस्ट पर निर्भर कारखाने के साथ निर्भरता अप्रत्यक्ष करें


तो ... आप एक परिवर्तनशील सिंगलटन के साथ निर्भरता इंजेक्शन की जगह लेते हैं? यह भयानक लगता है, आप स्वतंत्र परीक्षण कैसे चलाएंगे? क्या आप प्रत्येक परीक्षण के लिए एक नई प्रक्रिया खोलते हैं या उन्हें एक विशिष्ट अनुक्रम में बाध्य करते हैं? जावा मेरा मजबूत सूट नहीं है, क्या मुझे कुछ गलत समझ में आया?
nvoigt

जावा मावेन परियोजना में, साझा उदाहरण कोई समस्या नहीं है क्योंकि जूनियर इंजन डिफ़ॉल्ट रूप से प्रत्येक परीक्षण को अलग-अलग वर्ग लोडर में चलाएगा, लेकिन मैं इस समस्या का सामना कुछ IDE में करता हूं क्योंकि यह उसी श्रेणी लोडर का पुन: उपयोग कर सकता है। इस समस्या को हल करने के लिए, वर्ग एक रीसेट विधि प्रदान करता है जिसे प्रत्येक परीक्षण के बाद मूल स्थिति में रीसेट करने के लिए कहा जाता है। व्यवहार में, यह एक दुर्लभ स्थिति है कि विभिन्न परीक्षण अलग-अलग नकली का उपयोग करना चाहते हैं। हमने वर्षों से बड़े पैमाने पर परियोजनाओं के लिए इस पैटर्न को लागू किया है, सब कुछ आसानी से चलता है, हमने त्याग पोर्टेबिलिटी और परीक्षण क्षमता के बिना पठनीय, स्वच्छ कोड का आनंद लिया।
जियानवू चेन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.