2012-06-22 25 views
8

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

मैं पहली बार मेरी समाधान की संरचना, मेरे सवालों व्याख्या करेंगे तो:

मैं एक WPF परियोजना शामिल है: आवेदन स्टार्टअप पर

  • मेरे एकता कंटेनर प्रारंभ (bootstrapper) (App.xaml.cs में)
  • मेरे सभी एप्लिकेशन दृश्य (XAML)।

एक अन्य परियोजना ViewModel कहा जाता है इस में शामिल हैं:

  • सभी अपने आवेदन ViewModels।

    1. आवेदन स्टार्टअप
    2. एकता कंटेनर निर्माण और प्रकार के पंजीकरण: MainView और MainViewModel
    3. मेरे सभी ViewModels एक ViewModelBase जो एक ILogger संपत्ति

मेरे आरंभीकरण तर्क इस प्रकार है को उजागर करता है से विरासत मेरे मुख्य दृश्य को हल करें और इसे दिखाएं।

public MainView(MainViewModel _mvm) 
  1. मेरे MainViewModel अपने पैनलों में से प्रत्येक के लिए एक बच्चे की ViewModel है:

var window = Container.Resolve<MainView>();

window.Show();

मेरे MainView निर्माता अपने निर्माता में एक MainViewModel वस्तु प्राप्त करता है

public ToolboxViewModel ToolboxVM{get; set;} 
public SolutionExplorerViewModel SolutionExplorerVM { get; set; } 
public PropertiesViewModel PropertiesVM { get; set; } 
public MessagesViewModel MessagesVM { get; set; } 

और मैं एक विधि InitializePanels() कि पैनलों में से प्रत्येक initializes बनाने की योजना बना रहा हूँ।

अब मेरे प्रश्न: मेरा मेनव्यू मॉडेल। प्रारंभिक पैनल() उन सभी पैनलों को कैसे प्रारंभ कर सकता है? दिए गए निम्नलिखित विकल्पों:

विकल्प 1: मैन्युअल ViewModels प्रारंभ:

ToolboxVM = new ToolboxViewModel(); 
//Same for the rest of VM... 

विपक्ष:

  • मैं एकता कंटेनर का उपयोग नहीं कर रहा हूँ तो मेरे निर्भरता (जैसेILogger) स्वचालित रूप से
  • समाधान नहीं होता

विकल्प 2:

  • मुझे लगता है कि एकता सेटर निर्भरता पढ़ा है:

    [Dependency] 
    public ToolboxViewModel ToolboxVM{get; set;} 
    //... Same for rest of Panel VM's 
    

    विपक्ष: मेरे गुण व्याख्या द्वारा सेटर इंजेक्शन उपयोग इससे बचा जाना चाहिए क्योंकि वे इस मामले में एकता के साथ निर्भरता उत्पन्न करते हैं

  • मैंने यह भी पढ़ा है कि आपको यूनिट टेस्ट के लिए एकता का उपयोग करने से बचना चाहिए, तो इस यूनिट टेस्ट में इस निर्भरता को स्पष्ट कैसे करें? कई आश्रित गुण होने के बाद कॉन्फ़िगर करने के लिए एक दुःस्वप्न हो सकता है।

विकल्प 3:

public MainViewModel(ToolboxViewModel _tbvm, SolutionExploerViewModel _sevm,....) 

पेशेवरों:

    तो वे स्वचालित रूप से एकता कंटेनर से हल कर रहे हैं MainViewModel निर्माता के लिए सभी मेरी पैनल ViewModels पारित करने के लिए उपयोग एकता निर्माता इंजेक्शन
  • सृजन के समय निर्भरता स्पष्ट और स्पष्ट होगी, जो मेरे व्यू मॉडेल यूनिटटेस्ट बनाने में मदद कर सकती है।

विपक्ष:

  • इतने सारे निर्माता पैरामीटर होने को बहुत शीघ्र

विकल्प 4 बदसूरत हो सकता है: कंटेनर निर्माण पर मेरे सारे वीएम प्रकार का पंजीयन। तब मेरे MainViewModel के निर्माता इंजेक्शन के माध्यम से UnityContainer उदाहरण गुजर:

public MainViewModel(IUnityContainer _container) 

इस तरह मैं की तरह कुछ कर सकता है:

 Toolbox = _container.Resolve<ToolboxViewModel>(); 
     SolutionExplorer = _container.Resolve<SolutionExplorerViewModel>(); 
     Properties = _container.Resolve<PropertiesViewModel>(); 
     Messages = _container.Resolve<MessagesViewModel>(); 

विपक्ष:

  • अगर मैं तय उपयोग करने के लिए नहीं मेरे यूनिटटेस्ट्स के लिए एकता, जैसा कि कई लोग सुझाव देते हैं, तो मैं अपने पैनल व्यू मॉडेल को हल और प्रारंभ करने में सक्षम नहीं हूं।

यह देखते हुए कि लंबी व्याख्या, सबसे अच्छा तरीका क्या है ताकि मैं निर्भरता इंजेक्शन कंटेनर का लाभ उठा सकूं और यूनिट-टेस्टेबल समाधान के साथ समाप्त हो सकूं ??

अग्रिम धन्यवाद,

+0

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

+0

मेरा त्वरित और गंदा समाधान यह है कि संसाधनों के रूप में app.xaml में मेरे दृश्य मॉडल को तुरंत चालू करें, फिर आवश्यकतानुसार गठबंधन करें। ' 'जो यूनिट परीक्षण को सेट अप करने में आसान बनाता है। – Will

उत्तर

5

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

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

  • लिए # 3: बहुत से निर्माता निर्भरता आमतौर पर (facade classes में आम कार्यक्षमता समूहीकरण तथापि 4 नहीं है से कम है सभी के बाद)। आमतौर पर, उचित ढंग से डिज़ाइन किए गए कोड में यह समस्या नहीं होती है। ध्यान दें कि आपके MainViewModel पर निर्भर करता है कि आपको केवल बच्चे दृश्य मॉडल की सूची की आवश्यकता है, ठोस नहीं।
  • से # 4: आपको यूनिट परीक्षणों में आईओसी कंटेनर का उपयोग नहीं करना चाहिए। आप अपने MainViewModel (सीटीओआर के माध्यम से) मैन्युअल रूप से बनाते हैं और हाथ से द्वारा इंजेक्ट करते हैं।

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

साथ ही, जब आपके पास कार्यक्षमता से संबंधित समूह होते हैं तो दृश्यों को स्वैप करना बहुत आसान होता है। परियोजना संरचना इस तरह दिखाई देता है:

> MyApp.Users 
> MyApp.Users.ViewModels 
> MyApp.Users.Views 
> ... 

उपयोगकर्ता संपादन विंडो के लिए अलग दृष्टिकोण बाहर की कोशिश कर रहा recompiling और एकल विधानसभा (User.Views) की अदला-बदली की बात है। सभी एक बैग में दृष्टिकोण के साथ, आपको एप्लिकेशन के बहुत बड़े हिस्से को पुनर्निर्माण करना होगा, यहां तक ​​कि इसमें से अधिकांश भी बिल्कुल नहीं बदला है।

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

+1

मैन का सुझाव देते हैं, मैंने आपके उत्तर को 10 बार की तरह पढ़ा है, हर बार जब यह अधिक समझ में आता है। यह एक अविश्वसनीय आंख खोलने वाला है। प्रति 'मॉड्यूल' के अलग-अलग परियोजनाओं के निर्माण के बारे में समझ में आता है लेकिन मेरे पास पहले से ही डीएएल (डेटा एक्सेस लेयर), कॉमन, बीएलएल (बिजनेस लॉजिक लेयर), बिजनेस ऑब्जेक्ट्स, टेस्ट प्रोजेक्ट इत्यादि जैसी कई परियोजनाएं हैं ... –

+1

@AdolfoPerez: अगर यह है मामला, आपको * बदलते * मौजूदा बुनियादी ढांचे में समय और प्रयास पर विचार करना होगा। यदि आप इसे बर्दाश्त नहीं कर सकते हैं, तो बस ऐसा न करें - * "बैग" * संरचनाएं भी काम करती हैं, बस उन्हें समय के साथ बनाए रखने में थोड़ा और मुश्किल हो सकती है। मैंने इसे अपने जवाब में जोड़ा है इसलिए कोई गलतफहमी नहीं है। –

+0

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

2

सबसे पहले, आप शायद नहीं बल्कि ठोस वर्गों की तुलना में इंटरफेस का उपयोग करने, ताकि आप आप नकली वस्तुओं जब इकाई परीक्षण, यानी ToolboxViewModel के बजाय IToolboxViewModel पारित करने के लिए सक्षम हो जाएगा चाहते हैं, आदि

कहा जा रहा है कि, मैं तीसरे विकल्प - कन्स्ट्रक्टर इंजेक्शन की सिफारिश करता हूं।यह सबसे अधिक समझ में आता है, अन्यथा आप var mainVM = new MainViewModel() पर कॉल कर सकते हैं और एक गैर-कार्यात्मक दृश्य मॉडल के साथ समाप्त हो सकते हैं। ऐसा करने से, आप यह समझने में भी बहुत आसान बना रहे हैं कि आपके व्यू-मॉडल की निर्भरता क्या है, जो यूनिट परीक्षण लिखना आसान बनाता है।

मैं this link देखें, क्योंकि यह आपके प्रश्न के लिए प्रासंगिक है।

+0

धन्यवाद लेस्टर, हाँ मुझे लगता है कि विकल्प 3 और भी समझ में आता है, खासकर जब मुखौटा वर्गों के माध्यम से @jimmy_keen के रूप में कन्स्ट्रक्टर पैरामीटर को समूहीकृत करते हैं। आपकी सहायताके लिए धन्यवाद! सुनने के लिए इंतजार करेंगे कि अन्य लोग –

1

मैं लेस्टर के अंक से सहमत हूं लेकिन कुछ अन्य विकल्प और राय जोड़ना चाहता हूं।

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

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

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

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

+0

आपकी प्रतिक्रिया @bryanbcook के लिए धन्यवाद। वास्तव में, पीआरआईएसएम मेरे बुनियादी ढांचे विकल्पों में से एक था, मैंने क्षेत्रों और मॉड्यूल के बारे में कुछ उदाहरणों के माध्यम से जाना लेकिन मुझे लगता है कि इसमें PRISM ढांचे की गहरी समझ प्राप्त करने के लिए एक और अधिक महंगी सीखने की वक्र शामिल है। –

+0

प्रिज्म सिर्फ एक उदाहरण है कि जिम्मेदारियों को और तोड़ा जा सकता है। – bryanbcook

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

  • कोई संबंधित समस्या नहीं^_^