2012-05-08 27 views
7

मैं एक वस्तु लिख रहा हूं जिसमें हमेशा कुछ मूल्य होना चाहिए। सबसे विशेष रूप से, यह हमेशा Name संपत्ति के लिए एक मूल्य होना चाहिए।मैं ऑब्जेक्ट प्रारंभ करने को कैसे रोक सकता हूं?

public class User 
{ 
    public string Name { get; set; } 

    public User(string name) 
    { 
     Name = name; 
    } 
} 

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

public User(string name, IQueryable<User> allUsers) 
    { 
     var matches = allUsers.Where(q => q.Name == name).ToList(); 
     if(matches.Any()) 
     { 
      // abort object initialization 
     } 
     Name = name; 
    } 

लेकिन मुझे यकीन है कि कैसे मैं वस्तु प्रारंभ गर्भपात होता नहीं हूँ। वास्तव में, यह भी संभव है?

क्या कोई ऑब्जेक्ट प्रारंभ करने के लिए कोई तरीका है (यानी: वस्तु को शून्य पर सेट करें) या क्या इसे पूरा करने का एक बेहतर तरीका है?

+0

एक जानवर बल भी तरह से, को शून्य अशक्त क्षेत्रों के साथ वस्तु जोड़ने के लिए, वस्तु – RhysW

+2

को दूर यह सिर्फ नमूना कोड हो सकता है लेकिन इस मामले में ऐसा नहीं है करने के लिए सभी क्षेत्रों सेट ', इसलिए आपको' कहां 'से गुज़रना पड़ेगा। –

+0

@BrianRasmussen दोनों का एक ही परिणाम है, इसलिए यह वास्तव में व्यक्तिगत वरीयता है जिसका आप उपयोग करते हैं। दूसरी तरफ 'ToList' कॉल पूरी क्वेरी का मूल्यांकन न करने से 'किसी भी' की छोटी सर्किटिंग को रोकती है। – Servy

उत्तर

3

एक वस्तु का आरंभीकरण छोड़ रहा है निर्माता में एक अपवाद फेंक द्वारा किया जाता है, और अमान्य इनपुट अस्वीकार करने के लिए सिफारिश की है।

public class User 
{ 
    public User(String name) { 
     if (String.IsNullOrWhiteSpace(name)) { 
      if (name == null) { 
       throw new System.ArgumentNullException("Cannot be null.", "name"); 
      } 
      else { 
       throw new System.ArgumentException("Cannot be empty.", "name"); 
      } 
     } 
    } 
} 

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

public class User 
{ 
    private User(String name) { 
     if (String.IsNullOrWhiteSpace(name)) { 
      if (name == null) { 
       throw new System.ArgumentNullException("Cannot be null.", "name"); 
      } 
      else { 
       throw new System.ArgumentException("Cannot be empty.", "name"); 
      } 
     } 
    } 

    public static User CreateUser(String name) { 
     User user = new User(name); // Lightweight instantiation, basic validation 

     var matches = allUsers.Where(q => q.Name == name).ToList(); 

     if(matches.Any())   
     {   
      throw new System.ArgumentException("User with the specified name already exists.", "name");   
     }  

     Name = name; 
    } 

    public String Name { 
     get; 
     private set; // Optionally public if needed 
    } 
} 

आप देख सकते हैं कि कारखाना पैटर्न बेहतर फिट बैठता है, और क्योंकि यह एक विधि है एक फोन करने वाले कुछ काम यह लागू द्वारा चल रहा होने की उम्मीद कर सकते हैं। जबकि एक कन्स्ट्रक्टर के साथ एक हल्का वजन होने की उम्मीद करेगा।

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

public class User 
{ 
    public User(String name) { 
     if (String.IsNullOrWhiteSpace(name)) { 
      if (name == null) { 
       throw new System.ArgumentNullException("Cannot be null.", "name"); 
      } 
      else { 
       throw new System.ArgumentException("Cannot be empty.", "name"); 
      } 
     } 
    } 
} 

public class SomeDataSource { 
    public void AddUser(User user) { 
     // Do your business validation here, and either throw or possibly return a value 
     // If business rules pass, then add the user 
     Users.Add(user); 
    } 
} 
+0

इस तरह के विस्तृत उत्तर के लिए बहुत बहुत धन्यवाद! मुझे आपके द्वारा उल्लिखित फैक्टरी पैटर्न मार्ग पर जाने के लिए राजी किया गया है। – quakkels

+0

आपका बहुत स्वागत है, खुशी है कि मैं मदद कर सकता हूं। –

4

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

2

आपको उपयोगकर्ता बनाने से पहले डुप्लिकेट नाम की जांच करनी चाहिए।

+1

आप दौड़ की स्थिति प्राप्त कर सकते हैं। – jason

+0

आप हमेशा दौड़ की स्थिति प्राप्त कर सकते हैं :-) – Steven

6

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

2

निजी तौर पर, मैं तत्काल होने से पहले तर्क जांच चलाता हूं। उदाहरण के लिए:

if(UserLogic.PreInsertValidation(string username)){ 
    User newUser = new User(username); 
} 
else{ 
    // Handling - maybe show message on client "The username is already in use." 
} 

PreInsertValidation सभी व्यापार तर्क आपकी आवश्यकताओं के आधार चेकों होगा।

0

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

0

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

public static User CreateUser(string name) 
{ 
     // Check whether user already exists, if so, throw exception/return null 

     // If name didn't exist, record that this user name is now taken. 
     // Construct and return the user object 
     return new User(name); 
} 

private User(string name) 
{ 
     this.Name = name; 
} 

फिर अपने बुला कोड User myUser = User.CreateUser("Steve"); का उपयोग करें और उसके अनुसार अशक्त वापसी/अपवाद संभाल सकता:

3
बजाय एक सार्वजनिक निर्माता होने के

, इस तरह की एक विधि और एक निजी निर्माता है।

यह जोड़ना उचित है कि आप जो भी तरीका उपयोग कर रहे हैं, जो उपयोगकर्ता नाम लेते हैं, को यह कहने के लिए अद्यतन किया जाना चाहिए कि यह नाम CreateUser विधि के भीतर लिया गया है। अन्यथा, यदि आप इस ऑब्जेक्ट को डेटाबेस या किसी चीज़ में संग्रहीत करने से पहले थोड़ी देर प्रतीक्षा करते हैं, तो आपको अभी भी समस्याएं होंगी। मैंने यह स्पष्ट करने के लिए ऊपर दिए गए कोड को अपडेट किया है।

+1

इस तरह आप सभी उपयोगकर्ताओं को पकड़ने के लिए एक स्थिर 'IQueryable' का उपयोग कर सकते हैं (या सभी उपयोगकर्ताओं के भंडार का संदर्भ) और लॉकिंग के साथ दौड़ की स्थिति से बचें। – SWeko

+0

@SWeko इस विधि और दोनों कन्स्ट्रक्टर का उपयोग करते हुए दौड़ की स्थिति होती है यदि कोई ताले नहीं होते हैं, और दोनों उचित ताले जोड़कर दौड़ की स्थिति को हटा सकते हैं। – Servy

0

इसके बजाय वस्तु के अंदर ही इस सत्यापन कर के, एक सेवा में निर्माण, सत्यापन और इस संस्था की बचत डाल दिया। यह सेवा ValidationException फेंक सकती है जब उपयोगकर्ता का नाम अद्वितीय नहीं होता है, और यह भी सुनिश्चित करने के लिए लेनदेन शुरू कर सकता है कि कोई दौड़ स्थिति नहीं हो सकती है। एक अच्छा मॉडल जिसका उपयोग मैं command/handler पैटर्न है। यहां एक उदाहरण दिया गया है:

public class CreateNewUserCommand 
{ 
    public string UserName { get; set; } 
} 

internal class CreateNewUserCommandHandler 
    : ICommandHandler<CreateNewUserCommand> 
{ 
    private readonly IUnitOfWork uow; 

    public CreateNewUserCommandHandler(
     IUnitOfWork uow) 
    { 
     this.uow = uow; 
    } 

    public void Handle(CreateNewUserCommand command) 
    { 
     // TODO Validation 

     var user = new User { Name = command.Name }; 

     this.uow.Users.InsertOnSubmit(user); 
    } 
} 

आप अपनी कक्षा में भी सत्यापन को अतिरिक्त कर सकते हैं।

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