2012-04-25 22 views
11

सारनिर्भरता इंजेक्शन और विकास उत्पादकता

पिछले कुछ महीनों मैं एक हल्के वजन, एपीआई अमूर्त और इकाई/घटक/पटकथा प्रणाली के साथ सी # आधारित खेल इंजन प्रोग्रामिंग गया है। इसका पूरा विचार यूनिटी इंजन के समान आर्किटेक्चर प्रदान करके एक्सएनए, स्लिम डीएक्स और इस तरह के खेल विकास प्रक्रिया को कम करना है।

डिजाइन चुनौतियों

सबसे खेल डेवलपर्स जानते हैं, वहाँ अलग अलग सेवाओं आप अपने कोड भर का उपयोग करने की जरूरत है की एक बहुत कुछ कर रहे हैं। कई डेवलपर उदाहरण के वैश्विक स्थैतिक उदाहरणों का उपयोग करते हैं। एक रेंडर मैनेजर (या एक संगीतकार), एक दृश्य, ग्राफिक्सडेविस (डीएक्स), लॉगर, इनपुट स्थिति, व्यूपोर्ट, विंडो आदि। वैश्विक स्थैतिक उदाहरण/सिंगलेट्स के कुछ वैकल्पिक दृष्टिकोण हैं। एक प्रत्येक वर्ग को उन वर्गों का एक उदाहरण देना है, जिन्हें किसी कन्स्ट्रक्टर या कन्स्ट्रक्टर/प्रॉपर्टी निर्भरता इंजेक्शन (डीआई) के माध्यम से एक्सेस करने की आवश्यकता है, दूसरा एक वैश्विक सेवा लोकेटर का उपयोग करना है, जैसे स्ट्रक्चर मैप ऑब्जेक्ट फैक्ट्री जहां सेवा लोकेटर को आमतौर पर कॉन्फ़िगर किया जाता है एक आईओसी कंटेनर।

निर्भरता इंजेक्शन

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

मेरे समस्याओं

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

असल में डीआई/आईओसी करना नाटकीय रूप से मेरी उत्पादकता धीमा कर देता है और कुछ मामलों में कोड और वास्तुकला को और जटिल बनाता है। इसलिए मुझे अनिश्चितता है कि यह एक रास्ता है जिसे मुझे पालन करना चाहिए, या केवल पुरानी शैली के तरीके को छोड़ दें और करें।मैं एक ही जवाब की तलाश नहीं कर रहा हूं कि मुझे क्या करना चाहिए या नहीं करना चाहिए, लेकिन यदि वैश्विक स्थिर/सिंगलटन तरीके का उपयोग करने के विरोध में डीआई का उपयोग करना लंबे समय तक लायक है, तो संभावित पेशेवरों और विपक्षों को मैंने अनदेखा कर दिया है और DI से निपटने पर ऊपर सूचीबद्ध मेरी समस्याओं के संभावित समाधान।

उत्तर

19

क्या आप पुराने तरीके से वापस जाना चाहिए? मेरा उत्तर संक्षेप में नहीं है। आपके द्वारा उल्लेखित सभी कारणों से DI के कई लाभ हैं।

मैं अक्सर लगता है कि मैं इंटरफेस खातिर

के लिए इंटरफेस बनाने हूँ आप यह कर रहे हैं, तो आप Reused Abstractions Principle (RAP)

सेवा आवश्यकताओं, कुछ वर्गों 'कंस्ट्रक्टर्स के आधार पर उल्लंघन कर रही है बहुत बड़ा हो सकता है, जो कक्षा को अन्य संदर्भों में पूरी तरह से बेकार बना देगा जहां और यदि आईओसी का उपयोग नहीं किया जाता है। Single Reponsibility Principle:

तो अपनी कक्षाओं कंस्ट्रक्टर्स बहुत बड़ी और जटिल हैं, यह आप को दिखाने के लिए है कि आप एक बहुत ही महत्वपूर्ण अन्य सिद्धांत का उल्लंघन कर रहे सबसे अच्छा तरीका है। इस मामले में यह अलग-अलग वर्गों में आपके कोड को निकालने और दोबारा करने का समय है, सुझाए गए निर्भरताओं की संख्या लगभग 4 है।

DI करने के लिए आपको इंटरफ़ेस नहीं होना चाहिए, DI केवल वही तरीका है अपनी निर्भरता को अपनी वस्तु में प्राप्त करें। परीक्षण उद्देश्यों के लिए निर्भरता को प्रतिस्थापित करने में सक्षम होने के लिए इंटरफेस बनाना एक आवश्यक तरीका हो सकता है। जब तक निर्भरता का उद्देश्य है:

  1. आसान
  2. बाहरी उप (फाइल सिस्टम आदि)

आप एक सार वर्ग के रूप में अपनी निर्भरता बना सकते हैं से बात नहीं करता है अलग करने के लिए, या किसी भी वर्ग जहां विधियों को आप प्रतिस्थापित करना चाहते हैं वे आभासी हैं। हालांकि इंटरफेस एक निर्भरता का सबसे अच्छा डी-युग्मित तरीका बनाते हैं।

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

जहां तक ​​आईओसी कंटेनर की निर्भरता है, आपको अपने क्लाइंट क्लास में इसकी निर्भरता कभी नहीं होनी चाहिए। और उन्हें नहीं करना है।

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

वहाँ भी कई अन्य क्रिएशनल पैटर्न आप का अनुसरण कर सकते निर्माण आसान बनाने के लिए:

new SomeBusinessObject(
    new SomethingChangedNotificationService(new EmailErrorHandler()), 
    new EmailErrorHandler(), 
    new MyDao(new EmailErrorHandler())); 

आप एक ठोस कारखाना है कि जानता है कि कैसे करने के लिए बना सकते हैं: तो आप इस तरह के कई निर्भरता के साथ एक वस्तु का निर्माण करना चाहते हैं कहो इस का निर्माण:

public static class SomeBusinessObjectFactory 
{ 
    public static SomeBusinessObject Create() 
    { 
     return new SomeBusinessObject(
      new SomethingChangedNotificationService(new EmailErrorHandler()), 
      new EmailErrorHandler(), 
      new MyDao(new EmailErrorHandler())); 
    } 
} 

और फिर इस तरह इसका इस्तेमाल:

SomeBusinessObject bo = SomeBusinessObjectFactory.Create(); 

तुम भी गरीब मनुष्य di का उपयोग करें और एक निर्माता है कि सभी में कोई तर्क लेता बना सकते हैं:

public SomeBusinessObject() 
{ 
    var errorHandler = new EmailErrorHandler(); 
    var dao = new MyDao(errorHandler); 
    var notificationService = new SomethingChangedNotificationService(errorHandler); 
    Initialize(notificationService, errorHandler, dao); 
} 

protected void Initialize(
    INotificationService notifcationService, 
    IErrorHandler errorHandler, 
    MyDao dao) 
{ 
    this._NotificationService = notifcationService; 
    this._ErrorHandler = errorHandler; 
    this._Dao = dao; 
} 

तो यह सिर्फ लगता है जैसे कि यह काम करता था:

SomeBusinessObject bo = new SomeBusinessObject(); 

गरीब आदमी की डि का उपयोग करना माना जाता है बुरा जब आपका डिफ़ॉल्ट कार्यान्वयन बाह्य तृतीय पक्ष पुस्तकालयों में होता है, लेकिन जब आपके पास एक अच्छा डिफ़ॉल्ट कार्यान्वयन होता है तो कम बुरा होता है।

तो जाहिर है कि सभी डी कंटेनर, ऑब्जेक्ट बिल्डर्स और अन्य पैटर्न हैं।

तो आपको बस अपनी वस्तु के लिए एक अच्छा रचनात्मक पैटर्न के बारे में सोचना है। आपकी वस्तु को निर्भर नहीं करना चाहिए कि निर्भरताओं को कैसे बनाया जाए, वास्तव में यह उन्हें अधिक जटिल बनाता है और उन्हें 2 प्रकार के तर्क मिश्रण करने का कारण बनता है। इसलिए मैं डीआई का उपयोग करके बेकार नहीं हूं उत्पादकता में कमी होनी चाहिए।

कुछ विशेष मामले हैं जहां आपकी वस्तु को केवल एक उदाहरण इंजेक्शन नहीं मिल सकता है। जहां जीवनकाल आम तौर पर छोटा होता है और फ्लाई इंस्टेंस की आवश्यकता होती है। इस मामले में आप वस्तु में फैक्टरी इंजेक्षन चाहिए एक निर्भरता के रूप में:

public interface IDataAccessFactory 
{ 
    TDao Create<TDao>(); 
} 

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

public class ConcreteDataAccessFactory : IDataAccessFactory 
{ 
    private readonly IocContainer _Container; 

    public ConcreteDataAccessFactory(IocContainer container) 
    { 
     this._Container = container; 
    } 

    public TDao Create<TDao>() 
    { 
     return (TDao)Activator.CreateInstance(typeof(TDao), 
      this._Container.Resolve<Dependency1>(), 
      this._Container.Resolve<Dependency2>()) 
    } 
} 

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

संपादित करें: सार फैक्टरी निर्भरता के साथ वर्ग जोड़ना:

public class SomeOtherBusinessObject 
{ 
    private IDataAccessFactory _DataAccessFactory; 

    public SomeOtherBusinessObject(
     IDataAccessFactory dataAccessFactory, 
     INotificationService notifcationService, 
     IErrorHandler errorHandler) 
    { 
     this._DataAccessFactory = dataAccessFactory; 
    } 

    public void DoSomething() 
    { 
     for (int i = 0; i < 10; i++) 
     { 
      using (var dao = this._DataAccessFactory.Create<MyDao>()) 
      { 
       // work with dao 
       // Console.WriteLine(
       //  "Working with dao: " + dao.GetHashCode().ToString()); 
      } 
     } 
    } 
} 

मूल रूप से डि कर/आईओसी नाटकीय रूप से मेरी उत्पादकता धीमा कर देती है और कुछ मामलों में आगे कोड और वास्तुकला

मार्क पेचीदा सीमान ने इस विषय पर एक अद्भुत ब्लॉग लिखा, और प्रश्न का उत्तर दिया: इस तरह के प्रश्न के लिए मेरी पहली प्रतिक्रिया यह है: आप कहते हैं कि कमजोर युग्मित कोड को समझना मुश्किल है। क्या से कठिन है?

Loose Coupling and the Big Picture

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

क्या निर्भरताएं हैं?

  • ऐप्लिकेशन कॉन्फ़िगरेशन
  • सिस्टम संसाधन (घड़ी)
  • तीसरी पार्टी पुस्तकालय
  • डाटाबेस
  • WCF/नेटवर्क सेवा
  • बाहरी प्रणाली (फ़ाइल/ईमेल)

कोई उपर्युक्त वस्तुओं या सहयोगियों में से आपके नियंत्रण से बाहर हो सकता है और साइड ef का कारण बन सकता है व्यवहार में fects और अंतर और परीक्षण करने के लिए मुश्किल बनाते हैं। ये समय एक एब्स्ट्रक्शन (कक्षा/इंटरफेस) पर विचार करने और डीआई का उपयोग करने के लिए हैं।

निर्भरताएं क्या नहीं हैं, वास्तव में DI की आवश्यकता नहीं है?

  • List<T>
  • MemoryStream
  • स्ट्रिंग्स/प्रिमिटिव
  • पत्ता वस्तुओं/जैसे ऊपर बस जहां new कीवर्ड का उपयोग जरूरत instantiated जा सकती है Dto के

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

मैंने मार्क सीमान की पोस्ट के लिए बहुत से लिंक पोस्ट किए हैं, लेकिन मैं वास्तव में आपको अपनी पुस्तक और ब्लॉग पोस्ट पढ़ने की सलाह देता हूं।

+0

उत्कृष्ट उत्तर, सही विषय पर, धन्यवाद! मैंने आपके द्वारा लिंक किए गए दोनों लेख पढ़े हैं, जो दोनों जानकारी का एक बड़ा स्रोत हैं और वास्तव में मेरे मन में बहुत से अन्य प्रश्नों का उत्तर दिया। आरएपी उल्लंघन और एसआरपी मेरे मामले में ध्यान रखने के लिए अच्छी चीजों के रूप में खड़ा है। आपके उत्तर से एक आखिरी चीज खड़ी है, आप अपने क्लाइंट से आईओसी कंटेनर को छिपाने के लिए अमूर्त कारखानों का उपयोग करके उल्लेख करते हैं, क्या यह एक अमूर्त फैक्ट्री के लिए आईओसी कंटेनर पर निर्भर रहने के लिए ठीक है? – edvaldig

+1

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

+0

हाय, मैंने आपके लिए एक निर्भरता क्या है, समझने के बारे में अंत में एक और संपादन जोड़ा है, न कि उन वस्तुओं के लिए किसी भी निर्भरता प्रबंधन के बारे में चिंता करने के लिए जो सरल हैं और इसकी आवश्यकता नहीं है। – Andre

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