"वैकल्पिक" निर्भरता के साथ अलग-अलग घटकों में "सामान का एक भाग" उपयोगिता परियोजना को अलग करना


26

इन-हाउस परियोजनाओं के एक समूह के लिए C # /। NET का उपयोग करने के वर्षों में, हमारे पास एक पुस्तकालय में सामान की एक बड़ी संख्या में व्यवस्थित रूप से विकसित होने के लिए है। इसे "यूटिल" कहा जाता है, और मुझे यकीन है कि आप में से कई ने अपने करियर में इनमें से किसी एक जानवर को देखा होगा।

इस लाइब्रेरी के कई हिस्से बहुत ज्यादा स्टैंडअलोन हैं, और इन्हें अलग-अलग प्रोजेक्ट्स में विभाजित किया जा सकता है (जिसे हम ओपन-सोर्स करना चाहते हैं)। लेकिन एक बड़ी समस्या है जिसे अलग-अलग पुस्तकालयों के रूप में जारी किए जाने से पहले हल करने की आवश्यकता है। असल में, इन पुस्तकालयों के बीच "वैकल्पिक निर्भरता" कहे जाने वाले बहुत सारे मामले हैं।

इसे बेहतर ढंग से समझाने के लिए, कुछ ऐसे मॉड्यूलों पर विचार करें जो स्टैंड-अलोन लाइब्रेरी बनने के लिए अच्छे उम्मीदवार हैं। CommandLineParserकमांड लाइनों को पार्स करने के लिए है। XmlClassifyXML के लिए वर्गों को क्रमबद्ध करने के लिए है। PostBuildCheckसंकलित असेंबली पर जाँच करता है और विफल होने पर संकलन त्रुटि की रिपोर्ट करता है। ConsoleColoredStringरंगीन स्ट्रिंग शाब्दिक के लिए एक पुस्तकालय है। Lingoउपयोगकर्ता इंटरफेस का अनुवाद करने के लिए है।

उन पुस्तकालयों में से प्रत्येक का पूरी तरह से स्टैंड-अलोन इस्तेमाल किया जा सकता है, लेकिन अगर उन्हें एक साथ उपयोग किया जाता है तो उपयोगी अतिरिक्त सुविधाएँ होनी चाहिए। उदाहरण के लिए, दोनों CommandLineParserऔर XmlClassifyपर्दाफाश के बाद निर्माण की जाँच कार्यक्षमता, जिसके लिए आवश्यक है PostBuildCheck। इसी तरह, CommandLineParserरंग स्ट्रिंग स्ट्रिंग शाब्दिक का उपयोग करके विकल्प प्रदान करने की अनुमति देता है, आवश्यकता होती है ConsoleColoredString, और इसके माध्यम से अनुवाद योग्य प्रलेखन का समर्थन करता है Lingo

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

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

क्या .NET में ऐसी वैकल्पिक निर्भरता के प्रबंधन के लिए कोई स्थापित दृष्टिकोण हैं?


2
भले ही पुस्तकालय एक-दूसरे पर निर्भर हों, फिर भी उन्हें सुसंगत लेकिन अलग-अलग पुस्तकालयों में अलग करने में कुछ लाभ हो सकते हैं, जिनमें से प्रत्येक में कार्यक्षमता की एक व्यापक श्रेणी होती है।
रॉबर्ट हार्वे

जवाबों:


20

धीरे धीरे रिफ्लेक्टर।

उम्मीद करें कि इसे पूरा करने में कुछ समय लगेगा , और इससे पहले कि आप अपने यूटिल्स असेंबली को पूरी तरह से हटा सकें, कई पुनरावृत्तियों पर हो सकता है ।

समग्र दृष्टिकोण:

  1. पहले कुछ समय लें और सोचें कि आप कैसे इन यूटिलिटी असेंबलियों को देखना चाहते हैं जब आप कर चुके हों। अपने मौजूदा कोड के बारे में बहुत अधिक चिंता न करें, अंतिम लक्ष्य के बारे में सोचें। उदाहरण के लिए, आप की इच्छा कर सकते हैं:

    • MyCompany.Utilities.Core (एल्गोरिदम युक्त, लॉगिंग, आदि)
    • MyCompany.Utilities.UI (ड्राइंग कोड, आदि)
    • MyCompany.Utilities.UI.WinForms (System.Windows.Forms- संबंधित कोड, कस्टम नियंत्रण, आदि)
    • MyCompany.Utilities.UI.WPF (WPF- संबंधित कोड, MVVM आधार कक्षाएं)।
    • MyCompany.Utilities.Serialization (Serialization code)।
  2. इनमें से प्रत्येक प्रोजेक्ट के लिए खाली प्रोजेक्ट बनाएं, और उपयुक्त प्रोजेक्ट रेफरेंस बनाएं (UI संदर्भ कोर, यूआई.इनफॉर्म्स रेफरेंस यूआई), आदि।

  3. अपने यूटिल्स असेंबली से नए टारगेट असेंबली में कम-लटका हुआ फल (कक्षाएं या विधियां जो निर्भरता के मुद्दों से ग्रस्त नहीं हैं) में से किसी एक को स्थानांतरित करें।

  4. की कॉपी प्राप्त NDepend और मार्टिन फाउलर की पुनर्रचना विधानसभा अपने Utils का विश्लेषण मुश्किल वालों पर काम शुरू करने के लिए शुरू करने के लिए। दो तकनीकें जो मददगार होंगी:

वैकल्पिक इंटरफेस को संभालना

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

  1. सबसे पहले, अपने कोर असेंबली में आम इंटरफेस को परिभाषित करें:

    यहाँ छवि विवरण दर्ज करें

    उदाहरण के लिए, IStringColorerइंटरफ़ेस जैसा दिखेगा:

     namespace MyCompany.Utilities.Core.OptionalInterfaces
     {
         public interface IStringColorer
         {
             string Decorate(string s);
         }
     }
    
  2. फिर, सुविधा के साथ विधानसभा में इंटरफ़ेस को लागू करें। उदाहरण के लिए, StringColorerवर्ग ऐसा दिखेगा:

    using MyCompany.Utilities.Core.OptionalInterfaces;
    namespace MyCompany.Utilities.Console
    {
        class StringColorer : IStringColorer
        {
            #region IStringColorer Members
    
            public string Decorate(string s)
            {
                return "*" + s + "*";   //TODO: implement coloring
            }
    
            #endregion
        }
    }
    
  3. एक PluginFinder(या हो सकता है InterfaceFinder इस मामले में एक बेहतर नाम है) वर्ग है कि वर्तमान फ़ोल्डर में DLL फ़ाइलों से इंटरफेस पा सकते हैं। यहाँ एक सरल उदाहरण है। प्रति एडवायुडॉक की सलाह (और मैं सहमत हूं), जब आपकी परियोजनाएं बढ़ती हैं, तो मैं उपलब्ध उन्नत निर्भरता इंजेक्शन फ्रेमवर्क ( एकता और स्प्रिंगनेट के साथ कॉमन सीरिव लोकेटर ) का उपयोग करने का सुझाव दूंगा जो अधिक उन्नत के लिए अधिक मजबूत कार्यान्वयन के लिए "मुझे खोजें" उस सुविधा "क्षमताओं, अन्यथा सेवा लोकेटर पैटर्न के रूप में जाना जाता है । आप अपनी आवश्यकताओं के अनुरूप इसे संशोधित कर सकते हैं।

    using System;
    using System.Linq;
    using System.IO;
    using System.Reflection;
    
    namespace UtilitiesCore
    {
        public static class PluginFinder
        {
            private static bool _loadedAssemblies;
    
            public static T FindInterface<T>() where T : class
            {
                if (!_loadedAssemblies)
                    LoadAssemblies();
    
                //TODO: improve the performance vastly by caching RuntimeTypeHandles
    
                foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                {
                    foreach (Type type in assembly.GetTypes())
                    {
                        if (type.IsClass && typeof(T).IsAssignableFrom(type))
                            return Activator.CreateInstance(type) as T;
                    }
                }
    
                return null;
            }
    
            private static void LoadAssemblies()
            {
                foreach (FileInfo file in new DirectoryInfo(Directory.GetCurrentDirectory()).GetFiles())
                {
                    if (file.Extension != ".DLL")
                        continue;
    
                    if (!AppDomain.CurrentDomain.GetAssemblies().Any(a => a.Location == file.FullName))
                    {
                        try
                        {
                            //TODO: perhaps filter by certain known names
                            Assembly.LoadFrom(file.FullName);
                        }
                        catch { }
                    }
                }
            }
        }
    }
    
  4. अंत में, FindInterface विधि को कॉल करके अपने अन्य असेंबली में इन इंटरफेस का उपयोग करें। यहाँ एक उदाहरण है CommandLineParser:

    static class CommandLineParser
    {
        public static string ParseCommandLine(string commandLine)
        {
            string parsedCommandLine = ParseInternal(commandLine);
    
            IStringColorer colorer = PluginFinder.FindInterface<IStringColorer>();
    
            if(colorer != null)
                parsedCommandLine = colorer.Decorate(parsedCommandLine);
    
            return parsedCommandLine;
        }
    
        private static string ParseInternal(string commandLine)
        {
            //TODO: implement parsing as desired
            return commandLine;
        }
    

    }

सबसे महत्वपूर्ण: प्रत्येक परिवर्तन के बीच परीक्षण, परीक्षण, परीक्षण।


मैंने उदाहरण जोड़ा! :-)
केविन मैककॉर्मिक

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

@EdWoodcock अच्छा बिंदु एड, और मुझे विश्वास नहीं होता कि मैंने इसे लिखते समय सर्विस लोकेटर पैटर्न के बारे में नहीं सोचा था। PluginFinder निश्चित रूप से अपरिपक्व कार्यान्वयन है और एक DI फ्रेमवर्क यहाँ निश्चित रूप से काम करेगा।
केविन मैककॉर्मिक 15

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

@romkyns तो आप किस मार्ग पर जा रहे हैं? इसे छोड़कर-जैसा है? :)
मैक्स

5

आप एक अतिरिक्त पुस्तकालय में घोषित इंटरफेस का उपयोग कर सकते हैं।

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

यह विशेष रूप से MEF के साथ करना आसान है, क्योंकि यह इसके लिए पाठ्यपुस्तक का उपयोग है।

यह आपको पुस्तकालयों को संकलित करने की अनुमति देगा, उन्हें एन + 1 डीएल में विभाजित करने की कीमत पर।

HTH।


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

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

2

मैंने सोचा था कि हम सबसे व्यवहार्य विकल्प पोस्ट करेंगे जो हम अब तक देख चुके हैं, यह देखने के लिए कि क्या विचार हैं।

मूल रूप से, हम प्रत्येक घटक को शून्य संदर्भ के साथ एक पुस्तकालय में अलग करेंगे; सभी कोड जिन्हें एक संदर्भ की आवश्यकता होती है #if/#endif, उन्हें उपयुक्त नाम के साथ एक ब्लॉक में रखा जाएगा । उदाहरण के लिए, CommandLineParserउस हैंडल में कोड को ConsoleColoredStringरखा जाएगा #if HAS_CONSOLE_COLORED_STRING

कोई भी समाधान जो सिर्फ शामिल करना चाहता है CommandLineParserवह आसानी से कर सकता है, क्योंकि आगे कोई निर्भरता नहीं है। हालाँकि, यदि समाधान में ConsoleColoredStringपरियोजना भी शामिल है , तो प्रोग्रामर के पास अब विकल्प है:

  • में एक संदर्भ जोड़ने CommandLineParserके लिएConsoleColoredString
  • प्रोजेक्ट फ़ाइल में HAS_CONSOLE_COLORED_STRINGडिफाइन जोड़ें CommandLineParser

इससे संबंधित कार्यक्षमता उपलब्ध होगी।

इसके साथ कई मुद्दे हैं:

  • यह एक केवल स्रोत समाधान है; पुस्तकालय के प्रत्येक उपभोक्ता को इसे स्रोत कोड के रूप में शामिल करना चाहिए; वे सिर्फ एक बाइनरी शामिल नहीं कर सकते हैं (लेकिन यह हमारे लिए पूर्ण आवश्यकता नहीं है)।
  • लाइब्रेरी की लाइब्रेरी प्रोजेक्ट फ़ाइल में कुछ समाधान- संपादन संपादित होते हैं, और यह बिल्कुल स्पष्ट नहीं है कि यह परिवर्तन SCM के लिए कैसे प्रतिबद्ध है।

बल्कि सुंदर, लेकिन फिर भी, यह निकटतम है जो हम साथ आए हैं।

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


1

मैं .net में ब्राउनफील्ड एप्लिकेशन डेवलपमेंट पुस्तक की सिफारिश करने जा रहा हूं । दो सीधे प्रासंगिक अध्याय 8 और 9 हैं। अध्याय 8 आपके आवेदन को फिर से जारी करने के बारे में बात करता है, जबकि अध्याय 9 निर्भरता को नियंत्रित करने, नियंत्रण के व्युत्क्रम और परीक्षण पर इसके प्रभाव के बारे में बात करता है।


1

पूर्ण प्रकटीकरण, मैं एक जावा लड़का हूं। तो मैं समझता हूँ कि आप शायद उन तकनीकों की तलाश में नहीं हैं जिनका मैं यहाँ उल्लेख करूँगा। लेकिन समस्याएं समान हैं, इसलिए शायद यह आपको सही दिशा में ले जाएगा।

जावा में, कई बिल्ड सिस्टम हैं जो एक केंद्रीकृत विरूपण साक्ष्य भंडार के विचार का समर्थन करते हैं जो "कलाकृतियों" से बने घरों में हैं - मेरे ज्ञान के लिए यह .NET में GAC से कुछ हद तक अनुरूप है (कृपया मेरी अज्ञानता पर अमल करें यदि यह एक तनावपूर्ण एनाओलॉजी है) लेकिन इससे भी अधिक क्योंकि इसका उपयोग किसी भी समय स्वतंत्र रिपीटेबल बिल्ड बनाने के लिए किया जाता है।

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

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

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


0

शायद जॉन लैकोस की पुस्तक "लार्ज-स्केल सी ++ सॉफ्टवेयर डिज़ाइन" उपयोगी है (बेशक सी # और सी ++ या समान नहीं है, लेकिन आप पुस्तक से उपयोगी तकनीकों को खराब कर सकते हैं)।

मूल रूप से, पुनः-कारक और दो या दो से अधिक पुस्तकालयों का उपयोग करने वाले कार्यक्षमता को एक अलग घटक में स्थानांतरित करता है जो इन पुस्तकालयों पर निर्भर करता है। यदि आवश्यक हो, तो अपारदर्शी प्रकार आदि जैसी तकनीकों का उपयोग करें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.