2010-12-15 22 views
9

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

उत्तर

11

मैं अन्य कारणों से इनवेरिएंट्स के बारे में रोजर आल्सिंग के उत्तर के पूरक के लिए यह उत्तर दे रहा हूं।

सिमेंटिक जानकारी

रोजर स्पष्ट रूप से बताया कि संपत्ति setters अर्थ जानकारी ले नहीं है। Post.PublishDate जैसी संपत्ति के लिए एक सेटटर को अनुमति देना भ्रम जोड़ सकता है क्योंकि हम यह सुनिश्चित करने के लिए नहीं जानते कि पोस्ट प्रकाशित हो गया है या केवल प्रकाशन दिनांक सेट किया गया है या नहीं। हम यह सुनिश्चित करने के लिए नहीं जानते कि लेख प्रकाशित करने के लिए यह सब कुछ आवश्यक है। ऑब्जेक्ट "इंटरफेस" स्पष्ट रूप से अपना "इरादा" नहीं दिखाता है।

मुझे विश्वास है कि, "सक्षम" जैसे गुण "हो रही" और "सेटिंग" दोनों के लिए पर्याप्त अर्थपूर्ण हैं। यह ऐसा कुछ है जो तुरंत प्रभावी होना चाहिए और मैं संपत्ति सेटर पर लापता अर्थशास्त्र के अकेले कारण के लिए SetActive() या सक्रिय()/निष्क्रिय /() विधियों की आवश्यकता को नहीं देख सकता।

Invariants

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

संपत्ति क्रम प्रारंभ/स्थापित करने निर्भरता

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

public class Period 
    { 
     DateTime start; 
     public DateTime Start 
     { 
      get { return start; } 
      set 
      { 
       if (value > end && end != default(DateTime)) 
        throw new Exception("Start can't be later than end!"); 
       start = value; 
      } 
     } 

     DateTime end; 
     public DateTime End 
     { 
      get { return end; } 
      set 
      { 
       if (value < start && start != default(DateTime)) 
        throw new Exception("End can't be earlier than start!"); 
       end = value; 
      } 
     } 
    } 

यह "सेटर" सत्यापन का एक बेईमान उदाहरण है जो एक्सेस ऑर्डर निर्भरताओं का कारण बनता है। निम्नलिखित कोड इस समस्या को दिखाता है:

 public void CanChangeStartAndEndInAnyOrder() 
     { 
      Period period = new Period(DateTime.Now, DateTime.Now); 
      period.Start = DateTime.Now.AddDays(1); //--> will throw exception here 
      period.End = DateTime.Now.AddDays(2); 
      // the following may throw an exception depending on the order the C# compiler 
      // assigns the properties. 
      period = new Period() 
      { 
       Start = DateTime.Now.AddDays(1), 
       End = DateTime.Now.AddDays(2), 
      }; 
      // The order is not guaranteed by C#, so either way may throw an exception 
      period = new Period() 
      { 
       End = DateTime.Now.AddDays(2), 
       Start = DateTime.Now.AddDays(1), 
      }; 
     } 

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

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

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

ORM "जलयोजन" *

हाइड्रेशन, एक साधारण विचार में, मौजूदा डेटा वस्तुओं में वापस जाने का मतलब है। मेरे लिए यह संपत्ति सेटर्स के पीछे तर्क जोड़ने के साथ वास्तव में सबसे बड़ी समस्या है।

कई/अधिकतर ORMs संपत्ति में बने मूल्य को मानचित्र करते हैं। यदि आपके पास सत्यापन तर्क या तर्क है जो संपत्ति सेटर्स के अंदर ऑब्जेक्ट स्थिति (अन्य सदस्यों) को बदलता है तो आप एक "अधूरा" ऑब्जेक्ट (अभी भी लोड हो रहा है) के विरुद्ध सत्यापन करने की कोशिश कर रहे हैं। यह ऑब्जेक्ट प्रारंभिक समस्या के समान ही है क्योंकि आप "हाइड्रेटेड" फ़ील्ड को नियंत्रित नहीं कर सकते हैं।

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

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

निष्कर्ष

तो, अंत में, कोड दोहराव से बचने के, ORMs, के रूप में सबसे अच्छा वे कर सकते थे के रूप में प्रदर्शन के क्रम क्षेत्रों सेट कर रहे हैं पर निर्भर करता है आश्चर्य की बात अपवादों को रोकने के लिए अनुमति देते हैं, यह तर्क स्थानांतरित करने के लिए बुद्धिमान है संपत्ति सेटर्स से दूर।

मैं अभी भी यह पता लगा रहा हूं कि यह 'सत्यापन' तर्क होना चाहिए। हम ऑब्जेक्ट के इनवेरिएंट पहलुओं को कहां मान्य करते हैं? हम उच्च स्तरीय सत्यापन कहां डालते हैं? क्या हम प्रमाणीकरण (ऑनसेव, ऑनडिलेट, ...) करने के लिए ओआरएम पर हुक का उपयोग करते हैं? इत्यादि आदि इत्यादि। लेकिन यह इस उत्तर का दायरा नहीं है।

+1

तो आपके उदाहरण में हम सेटर्स से बचने के लिए 'period.setDateBoundries (startDate, endDate)' जैसे एक फ़ंक्शन का उपयोग करना चाहिए? एक ऐसा फ़ंक्शन जो डमी सेटर्स के बजाय व्यवसाय का अर्थ रखता है? – Songo

+1

@ सोंगो मैं यही करूँगा। लेकिन फिर कोई शिकायत कर सकता है कि दोनों तिथियों को केवल एक ही बदलने के लिए पास किया जाए (कल्पना करें कि क्या हम एक अवधि का विस्तार करना चाहते हैं, हमें दोनों तिथियां पारित करनी होंगी ... लेकिन फिर, हे, हम विस्तार विधि क्यों नहीं बनाते ?) इस तरह सोचें: जब भी आप किसी संपत्ति पर समाप्त होते हैं जो दूसरे पर निर्भर होता है, तो शायद उन्हें बदलने के तरीकों को पेश करना एक अच्छा विचार है या विधियों जो अधिक अर्थपूर्ण मूल्य जोड़ देंगे (अवधि की तरह। एक भेजें ...)! – Loudenvier

+0

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

1

एक सेटर बस एक मान निर्धारित करता है। यह "heavy" with logic नहीं होना चाहिए।

आपके ऑब्जेक्ट्स पर अच्छे वर्णनात्मक नाम वाले तरीके "heavy" with logic हो सकते हैं और डोमेन में एनालॉग होना चाहिए।

7

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

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

असल में इसका मतलब यह है कि जब आप राज्य को निजी रूप से रखते हैं तो आप सार्वजनिक रूप से अपनी संस्थाओं पर व्यवहार का पर्दाफाश करते हैं।

+0

और यह फिर से एक सेटर होगा। लेकिन इस मामले में आपके पास संपत्ति के बजाय एक विधि (या एक संदेश उपभोक्ता) है और यह आपके डोमेन ऑब्जेक्ट को सक्रिय होने के लिए बनाता है (केवल डीटीओ नहीं)। और लेख में http://martinfowler.com/bliki/AnemicDomainModel.html –

+0

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

+0

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

2

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

मुझे पता है कि यह बुरा अभ्यास है, और आपके पास शायद एक दृश्य मॉडल (जैसा कि एमवीवीएम में) या जैसा होना चाहिए, लेकिन कुछ छोटे ऐप्स के लिए यह आईएमएचओ को अधिक पैटर्न बनाने के लिए समझ में आता है।

गुणों का उपयोग करना .net में बॉक्स डेटा बाइंडिंग कार्यों के बाहर है। हो सकता है कि संदर्भ यह निर्धारित करता है कि डेटा बाध्यकारी दोनों तरीकों से काम करना चाहिए, और इसलिए इनोटिफाप्रोपर्टी को कार्यान्वित करना और सेटर तर्क के हिस्से के रूप में PropertyChanged को बढ़ा देना समझ में आता है।

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

मैं decoupling, अपरिवर्तनीयता, और इतनी बड़ी हद तक उचित ठहराने की कोशिश कर रहा हूं। मैं बस इतना कह रहा हूं कि कुछ संदर्भों में एक सरल वास्तुकला के लिए बुलाया जाता है।

+0

+1 मैं आपके द्वारा सूचीबद्ध सटीक कारणों के लिए अवधारणा के साथ संघर्ष कर रहा हूं। कुछ सीएसएलए के साथ सिल्वरलाइट एमवीवीएम और पुराने अनुभव से परिचित हूं। अन्य .NET डेवलपर्स से टिप्पणियां देखना पसंद करेंगे। – BuddyJoe

10

सेटर्स में कोई अर्थपूर्ण जानकारी नहीं है।

उदा।

blogpost.PublishDate = DateTime.Now; 

क्या इसका मतलब है कि पोस्ट प्रकाशित किया गया है? या सिर्फ प्रकाशित दिनांक सेट किया गया है?

पर विचार करें:

blogpost.Publish(); 

यह स्पष्ट रूप से उद्देश्य यह है कि ऐसे ब्लॉग पोस्ट को प्रकाशित किया जाना चाहिए पता चलता है।

इसके अलावा, सेटर्स ऑब्जेक्ट इनवेरिएंट तोड़ सकते हैं। उदाहरण के लिए यदि हमारे पास "त्रिकोण" इकाई है, तो इनवेरिएंट होना चाहिए कि सभी कोणों का योग 180 डिग्री होना चाहिए।

Assert.AreEqual (t.A + t.B + t.C ,180); 

अब अगर हम setters है, हम आसानी से अपरिवर्तनशीलताओं तोड़ सकते हैं:

t.A = 0; 
t.B = 0; 
t.C = 0; 

तो हम एक त्रिकोण है, जहां सभी कोणों का योग 0 रहे हैं ... कि वास्तव में एक त्रिभुज है? मैं नहीं कहूंगा। तरीकों के साथ

की जगह setters अपरिवर्तनशीलताओं बनाए रखने के लिए हमें मजबूर कर सकता है: कि इस अपनी इकाई के लिए एक अमान्य स्थिति का कारण बनता है

t.SetAngles(0,0,0); //should fail 

इस तरह के कॉल एक अपवाद कह फेंक चाहिए।

तो आप सेटरिक्स के बजाय विधियों के साथ अर्थशास्त्र और इनवेरिएंट प्राप्त करते हैं।

+0

इसलिए जब तक मैं सेटर्स का उपयोग कर एक invariant तोड़ नहीं रहा हूँ ठीक है? मेरा मतलब है कि यदि कोई उपयोगकर्ता ग्राहक की जानकारी संपादित कर रहा है (पहला नाम, मध्य नाम, अंतिम नाम) और एकमात्र invariant उनमें से कोई भी खाली या शून्य नहीं हो सकता है तो प्रत्येक फ़ील्ड के लिए एक सेटटर का उपयोग स्वीकार्य है? – Songo

-1

मैं यहां से हो सकता हूं, लेकिन मेरा मानना ​​है कि सेटटर विधियों के विपरीत सेटर्स को सेट करने के लिए उपयोग किया जाना चाहिए। मेरे पास इसके कुछ कारण हैं।

ए) यह नेट में समझ में आता है। प्रत्येक डेवलपर गुणों के बारे में जानता है। इस तरह आप किसी ऑब्जेक्ट पर चीजें सेट करते हैं। डोमेन ऑब्जेक्ट्स के लिए उससे क्यों विचलित हो जाते हैं?

बी) सेटर्स कोड हो सकता है। 3.5 से पहले मुझे विश्वास है, की स्थापना एक वस्तु एक आंतरिक चर और एक संपत्ति हस्ताक्षर

private object _someProperty = null; 
public object SomeProperty{ 
    get { return _someProperty; } 
    set { _someProperty = value; } 
} 

यह बहुत आसान है और imo सुरुचिपूर्ण सेटर में मान्यता डाल करने के लिए है शामिल थे। आईएल में, गेटर्स और सेटर्स को वैसे भी तरीकों में परिवर्तित कर दिया जाता है। डुप्लिकेट कोड क्यों?

ऊपर पोस्ट की गई प्रकाशित() विधि के उदाहरण में, मैं पूरी तरह से सहमत हूं। ऐसे समय होते हैं जब हम नहीं चाहते कि अन्य डेवलपर्स संपत्ति स्थापित करें। इसे एक विधि द्वारा संभाला जाना चाहिए। हालांकि, क्या यह हर संपत्ति के लिए एक सेटटर विधि रखने के लिए समझ में आता है जब .Net संपत्ति की घोषणा में हमें आवश्यक सभी कार्यक्षमता प्रदान करता है?

यदि आपके पास कोई व्यक्ति वस्तु है, तो कोई कारण नहीं होने पर हर संपत्ति के लिए विधियां क्यों बनाएं?

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