2010-04-04 22 views
8

मी और दो अन्य सहयोगी यह समझने की कोशिश कर रहे हैं कि प्रोग्राम को सर्वोत्तम तरीके से कैसे डिजाइन किया जाए। उदाहरण के लिए, मैं एक अंतरफलक ISoda और कई वर्गों कि Coke, Pepsi, DrPepper, आदि जैसे कि इंटरफ़ेस को लागू .... हैनिर्भरता इंजेक्शन का उपयोग करें

मेरे सहयोगी कह रहा है कि यह एक कुंजी/मान की तरह एक डेटाबेस में इन वस्तुओं डाल करने के लिए सबसे अच्छा है जोड़ी। उदाहरण के लिए:

Key  | Name 
-------------------------------------- 
Coke  | my.namespace.Coke, MyAssembly 
Pepsi  | my.namespace.Pepsi, MyAssembly 
DrPepper | my.namespace.DrPepper, MyAssembly 

... फिर XML विन्यास फाइल है कि सही कुंजी के लिए इनपुट नक्शा है, कुंजी के लिए डेटाबेस क्वेरी, तो वस्तु बनाने के।

मेरे पास कोई विशिष्ट कारण नहीं है, लेकिन मुझे लगता है कि यह एक खराब डिज़ाइन है, लेकिन मुझे नहीं पता कि क्या कहना है या इसके खिलाफ सही तरीके से तर्क कैसे देना है।

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

ISoda soda; 

switch (input) 
{ 
    case "Coke": 
     soda = new Coke(); 
     break;  
    case "Pepsi": 
     soda = new Pepsi(); 
     break; 
    case "DrPepper": 
     soda = new DrPepper(); 
     break; 
} 

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

मैं अपने सहयोगियों को किसी अन्य विधि की कोशिश करने के लिए टेबल पर किस प्रकार के तर्क ला सकता हूं? पेशेवर/विपक्ष क्या हैं? हमें इसे एक तरह क्यों करना चाहिए?

दुर्भाग्यवश, मेरे सहयोगी बदलने के लिए बहुत प्रतिरोधी हैं इसलिए मैं यह समझने की कोशिश कर रहा हूं कि मैं उन्हें कैसे समझा सकता हूं।

संपादित करें:

लगता है मेरे सवाल की तरह एक छोटा सा समझने के लिए मुश्किल है। हम यह पता लगाने की कोशिश कर रहे हैं कि कई इंटरफेस का उपयोग करने वाले एप्लिकेशन को सर्वोत्तम तरीके से डिज़ाइन करना है और इसमें उन इंटरफेस को लागू करने वाले कई ठोस प्रकार शामिल हैं।

हाँ, मुझे पता है कि डेटाबेस में यह सामान संग्रहीत करना एक बुरा विचार है, लेकिन हम बस एक बेहतर तरीके से नहीं जानते हैं। यही कारण है कि मैं पूछ रहा हूं कि कैसे हम इसे बेहतर करते हैं।

public void DrinkSoda(string input) 
{ 
    ISoda soda = GetSoda(input); 
    soda.Drink(); 
} 

हम GetSoda को सही तरीके से कैसे कार्यान्वित करते हैं? क्या हमें पूरे डिजाइन पर पुनर्विचार करना चाहिए? अगर इस तरह के जादू तारों को पार करना एक बुरा विचार है, तो हमें इसे कैसे करना चाहिए?

उपयोगकर्ता इनपुट जैसे "आहार कोक", "कोक", "कोक लाइम", या जो भी Coke के प्रकार को तत्काल करेगा, तो एकाधिक कीवर्ड एक ही प्रकार के लिए मानचित्र होते हैं।

बहुत से लोगों ने कहा है कि उपरोक्त पोस्ट कोड खराब है। मुझे पता है कि यह बुरा है। मैं अपने सहयोगियों को पेश करने के कारणों की तलाश कर रहा हूं और तर्क देता हूं कि यह क्यों बुरा है और हमें क्यों बदलने की जरूरत है।

मैं क्षमा चाहता हूं कि यह स्पष्टीकरण अभी भी समझना मुश्किल है। मेरे लिए स्थिति का वर्णन करना मुश्किल है क्योंकि मैं वास्तव में समझ में नहीं आता कि इसे शब्दों में कैसे रखा जाए।

+3

यह समझना वाकई मुश्किल है कि आप जिस वास्तविक समस्या को हल करने की कोशिश कर रहे हैं वह है। – mquander

+1

कोड का आपका दूसरा ब्लॉक फ़ैक्टरी पैटर्न का उपयोग करते समय दिखाई देने वाली चीज़ जैसा दिखता है। क्या आप पूछ रहे हैं कि आपके पास पास किए गए आईएसओड के प्रकार का निर्धारण कैसे किया जाता है? – NitroxDM

+2

वाह - डेटाबेस रिकॉर्ड में शब्दों को मैप करने के लिए एक्सएमएल फ़ाइल का उपयोग करके डेटाबेस में टाइपिंग नाम, और फिर उन्हें तुरंत चालू करने के लिए प्रतिबिंब का उपयोग करना - ** ** ** ** ** ** एंटरप्राइज़ है! ** – Aaronaught

उत्तर

4

निर्भरता इंजेक्शन के लिए सबसे अच्छा परिचय जो मैंने पार किया है वास्तव में Ninject विकी साइट पर quick series of articles that form a walkthrough of the concepts है।

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

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

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

3

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

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

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

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

स्विच-केस समाधान ठीक है, क्योंकि यह फैक्ट्री पैटर्न को लागू करने का एक तरीका है। अधिकांश प्रोग्रामर इसे समझने के लिए कड़ी मेहनत नहीं करेंगे। यह आपके कोड के चारों ओर तत्काल उप-वर्गों की तुलना में अधिक स्पष्ट रूप से व्यवस्थित है।

+0

यह कारणों में से एक है कि मुझे डीआई कंटेनर के रूप में निनजेक्ट पसंद है। पंजीकरण एक धाराप्रवाह इंटरफेस का उपयोग कर कोड मॉड्यूल में किया जाता है - एक्सएमएल कॉन्फ़िगरेशन फ़ाइल या डेटाबेस नहीं। ऑटोफैक के साथ ही। – Aaronaught

+3

@ हारूनॉट, मुझे लगता है कि Spring.NET को छोड़कर कोई कंटेनर, सहित। एकता इन-कोड पंजीकरण कर सकती है, उनमें से अधिकतर (एकता और वसंत को छोड़कर) सम्मेलन आधारित पंजीकरण कर सकती हैं, इसलिए मुझे नहीं लगता कि यह इतना बड़ा सौदा है - यह वास्तव में मानक है। –

3

प्रत्येक डी लाइब्रेरी के लिए इसका अपना वाक्यविन्यास है।Ninject ऐसा दिखाई देता है:

public class DrinkModule : NinjectModule 
{ 
    public override void Load() 
    { 
     Bind<IDrinkable>() 
      .To<Coke>() 
      .Only(When.ContextVariable("Name").EqualTo("Coke")); 
     Bind<IDrinkable>() 
      .To<Pepsi>() 
      .Only(When.ContextVariable("Name").EqualTo("Pepsi")); 
     // And so on 
    } 
} 
बेशक

, यह बहुत मैपिंग बनाए रखने के लिए कठिन हो जाता है, इसलिए यदि मैं इस प्रकार की प्रणाली है तो मैं आमतौर पर this one के लिए एक साथ समान एक प्रतिबिंब आधारित पंजीकरण डाल अंत:

public class DrinkReflectionModule : NinjectModule 
{ 
    private IEnumerable<Assembly> assemblies; 

    public DrinkReflectionModule() : this(null) { } 

    public DrinkReflectionModule(IEnumerable<Assembly> assemblies) 
    { 
     this.assemblies = assemblies ?? 
      AppDomain.CurrentDomain.GetAssemblies(); 
    } 

    public override void Load() 
    { 
     foreach (Assembly assembly in assemblies) 
      RegisterDrinkables(assembly); 
    } 

    private void RegisterDrinkables(Assembly assembly) 
    { 
     foreach (Type t in assembly.GetExportedTypes()) 
     { 
      if (!t.IsPublic || t.IsAbstract || t.IsInterface || t.IsValueType) 
       continue; 
      if (typeof(IDrinkable).IsAssignableFrom(t)) 
       RegisterDrinkable(t); 
     } 
    } 

    private void RegisterDrinkable(Type t) 
    { 
     Bind<IDrinkable>().To(t) 
      .Only(When.ContextVariable("Name").EqualTo(t.Name)); 
    } 
} 

थोड़ा अधिक कोड लेकिन आपको इसे अपडेट नहीं करना है, यह स्वयं पंजीकरण हो जाता है।

ओह, और आप इस तरह इसका इस्तेमाल:

var pop = kernel.Get<IDrinkable>(With.Parameters.ContextVariable("Name", "Coke")); 

जाहिर है यह एक एकल डि पुस्तकालय (Ninject) के लिए विशिष्ट है, लेकिन उनमें से सभी चर या पैरामीटर की धारणा का समर्थन है, और वहाँ बराबर पैटर्न हैं उन सभी को (Autofac, विंडसर, एकता, आदि) के लिए

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

+0

हां, मुझे एहसास है कि पोस्ट कोड के पास DI के साथ कुछ लेना देना नहीं है। मैं अच्छे कारणों से पूछ रहा हूं कि इस तरह के परिदृश्यों को संभालने के लिए हमें डीआई पर क्यों जाना चाहिए या नहीं। मैं पूरी तरह से सहमत हूं कि पोस्ट कोड खराब है, और मुझे अपने सहयोगियों को पेश करने के लिए अच्छे तर्क चाहिए। – Rune

+0

@Rune: मैंने आपका संपादन पढ़ लिया है और मुझे लगता है कि मेरा जवाब अनिवार्य रूप से वही है; यदि आप ऐसा करने के लिए एक बेहतर तरीका ढूंढ रहे हैं तो मेरा जवाब * डी लाइब्रेरी * का उपयोग करेगा। पक्ष में प्राथमिक तर्क है (ए) रखरखाव और (बी) यह * आसान * है। और संभवतः (सी) यह आपके द्वारा लिखे गए कुछ की तुलना में छोटी गाड़ी होने की संभावना कम है। यदि उनमें से कोई भी चीज़ आपके लिए महत्वपूर्ण नहीं है तो मैं केवल फैक्ट्री विधि का उपयोग करूंगा। – Aaronaught

7

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

उदाहरण के तौर पर, आपको एक आईएसओडी इंस्टेंस में स्ट्रिंग के रूप में उपयोगकर्ता इनपुट का अनुवाद करने की आवश्यकता है।

ऐसा करने का मानक, ढीला युग्मित तरीका Abstract Factory को नियोजित करके है। इस तरह यह निर्धारित करें:

public interface ISodaFactory 
{ 
    ISoda Create(string input); 
} 

आपका उपभोक्ता अब एक निर्भरता ISodaFactory पर इस तरह से लेते हैं और इसका इस्तेमाल कर सकते हैं,:

public class SodaConsumer 
{ 
    private readonly ISodaFactory sodaFactory; 

    public SodaConsumer(ISodaFactory sodaFactory) 
    { 
     if (sodaFactory == null) 
     { 
      throw new ArgumentNullException(sodaFactory); 
     } 

     this.sodaFactory = sodaFactory; 
    } 

    public void DrinkSoda(string input) 
    { 
     ISoda soda = GetSoda(input); 
     soda.Drink(); 
    } 

    private ISoda GetSoda(input) 
    { 
     return this.sodaFactory.Create(input); 
    } 
} 

सूचना कैसे गार्ड खण्ड के संयोजन और readonly कीवर्ड SodaConsumer की गारंटी देता है कक्षा 'invariants, जो आपको चिंता के बिना निर्भरता का उपयोग करने देता है कि यह शून्य हो सकता है।

+0

हो सकता है कि मैंने सिर्फ सवाल को गलत समझा, लेकिन मैंने सोचा कि वह 'ISodaFactory.Create' विधि को कार्यान्वित करने के तरीके से पूछ रहा था - जहां वह सभी' स्विच 'कथन या प्रतिबिंब कोड चला गया है। – Aaronaught

4

यह पैटर्न मैं आम तौर पर उपयोग करते हैं मैं एक नाम या अन्य धारावाहिक डेटा के आधार पर सही वर्ग का दृष्टांत की जरूरत है:

public interface ISodaCreator 
{ 
    string Name { get; } 
    ISoda CreateSoda(); 
} 

public class SodaFactory 
{ 
    private readonly IEnumerable<ISodaCreator> sodaCreators; 

    public SodaFactory(IEnumerable<ISodaCreator> sodaCreators) 
    { 
     this.sodaCreators = sodaCreators; 
    } 

    public ISoda CreateSodaFromName(string name) 
    { 
     var creator = this.sodaCreators.FirstOrDefault(x => x.Name == name); 
     // TODO: throw "unknown soda" exception if creator null here 

     return creator.CreateSoda(); 
    } 
} 

ध्यान दें कि आप अभी भी ISodaCreator कार्यान्वयन का एक बहुत बनाने के लिए की आवश्यकता होगी, और किसी भी तरह SodaFactory कन्स्ट्रक्टर को पास करने के लिए सभी कार्यान्वयन एकत्रित करें। काम है कि में शामिल कम करने के लिए, आप प्रतिबिंब के आधार पर एक भी सामान्य सोडा निर्माता बना सकते हैं:

public ReflectionSodaCreator : ISodaCreator 
{ 
    private readonly ConstructorInfo constructor; 

    public string Name { get; private set; } 

    public ReflectionSodaCreator(string name, ConstructorInfo constructor) 
    { 
     this.Name = name; 
     this.constructor = constructor; 
    } 

    public ISoda CreateSoda() 
    { 
     return (ISoda)this.constructor.Invoke(new object[0]) 
    } 
} 

और फिर बस को क्रियान्वित विधानसभा (या विधानसभाओं के कुछ अन्य संग्रह) स्कैन सभी सोडा प्रकार खोजने के लिए और एक का निर्माण SodaFactory इस तरह:

IEnumerable<ISodaCreator> sodaCreators = 
    from type in Assembly.GetExecutingAssembly().GetTypes() 
    where type.GetInterface(typeof(ISoda).FullName) != null 
    from constructor in type.GetConstructors() 
    where constructor.GetParameters().Length == 0 
    select new ReflectionSodaCreator(type.Name, constructor); 
sodaCreators = new List<ISodaCreator>(sodaCreators); // evaluate only once 
var sodaFactory = new SodaFactory(sodaCreators); 

ध्यान दें कि मेरा उत्तर पूरक कि मार्क सीनैन की: वह ग्राहकों एक सार ISodaFactory उपयोग करते हैं, ताकि वे के बारे में कैसे सोडा कारखाना काम करता है देखभाल करने के लिए नहीं है यह बताने के लिए कह रहा है।मेरा जवाब ऐसे सोडा कारखाने का एक उदाहरण कार्यान्वयन है।

3

मुझे लगता है कि समस्या भी एक कारखाना या जो भी नहीं है; यह आपकी कक्षा संरचना है।

DrPepperPepsi से भिन्न व्यवहार करता है? और Coke? मेरे लिए, ऐसा लगता है कि आप इंटरफेस का अनुचित तरीके से उपयोग कर रहे हैं।

स्थिति के आधार पर, मैं कक्षा को BrandName या इसी तरह के संपत्ति के साथ बनाउंगा। BrandName को DrPepper या Coke पर सेट किया जाएगा, या तो enum या string (पूर्व आपके मामले के लिए बेहतर समाधान की तरह लगता है) के माध्यम से सेट किया जाएगा।

कारखाने की समस्या को हल करना आसान है। वास्तव में, आपको बिल्कुल एक कारखाने की आवश्यकता नहीं है। बस Soda कक्षा के निर्माता का उपयोग करें।

+0

हां, वे अलग-अलग व्यवहार करेंगे। मैंने बस अपना उदाहरण जितना संभव हो उतना सामान्य बनाने की कोशिश की। लेकिन असली दुनिया में, इंटरफेस विधि "पेय" प्रत्येक सोडा के लिए अलग-अलग लागू की जाएगी। – Rune

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