की मान्यता लागू करने के लिए struct का उपयोग में निर्मित प्रकार


9

सामान्य रूप से डोमेन ऑब्जेक्ट में ऐसे गुण होते हैं जिन्हें एक अंतर्निहित प्रकार द्वारा दर्शाया जा सकता है, लेकिन जिनके वैध मान उन मानों का एक सबसेट होते हैं जिन्हें उस प्रकार द्वारा दर्शाया जा सकता है।

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

इसे हल करने का एक तरीका मूल्य को एक रिवाज के रूप में संग्रहीत करना है structजिसमें private readonlyअंतर्निहित प्रकार का एक एकल समर्थन क्षेत्र है और जिसका निर्माता प्रदान किए गए मूल्य को सत्यापित करता है। हम तो हमेशा ही इस का उपयोग करके मान्य मानों का उपयोग कर के बारे में सुनिश्चित किया जा सकता है structप्रकार।

हम कास्ट ऑपरेटर्स को अंतर्निहित अंतर्निहित प्रकार से भी प्रदान कर सकते हैं ताकि मान मूल रूप से दर्ज कर सकें और अंतर्निहित प्रकार से बाहर निकल सकें।

एक उदाहरण के रूप में एक ऐसी स्थिति को लें, जहां हमें एक डोमेन ऑब्जेक्ट के नाम का प्रतिनिधित्व करने की आवश्यकता है, और वैध मान किसी भी स्ट्रिंग हैं जो लंबाई में 1 और 255 वर्णों के बीच है। हम निम्नलिखित संरचना का उपयोग करके इसका प्रतिनिधित्व कर सकते हैं:

public struct ValidatedName : IEquatable<ValidatedName>
{
    private readonly string _value;

    private ValidatedName(string name)
    {
        _value = name;
    }

    public static bool IsValid(string name)
    {
        return !String.IsNullOrEmpty(name) && name.Length <= 255;
    }

    public bool Equals(ValidatedName other)
    {
        return _value == other._value;
    }

    public override bool Equals(object obj)
    {
        if (obj is ValidatedName)
        {
            return Equals((ValidatedName)obj);
        }
        return false;
    }

    public static implicit operator string(ValidatedName x)
    {
        return x.ToString();
    }

    public static explicit operator ValidatedName(string x)
    {
        if (IsValid(x))
        {
            return new ValidatedName(x);
        }
        throw new InvalidCastException();
    }

    public static bool operator ==(ValidatedName x, ValidatedName y)
    {
        return x.Equals(y);
    }

    public static bool operator !=(ValidatedName x, ValidatedName y)
    {
        return !x.Equals(y);
    }

    public override int GetHashCode()
    {
        return _value.GetHashCode();
    }

    public override string ToString()
    {
        return _value;
    }
}

उदाहरण दिखाता है कि stringजैसे- implicitजैसे यह विफल हो सकता है, लेकिन से- stringकास्ट के रूप explicitमें यह अमान्य मूल्यों के लिए फेंक देगा, लेकिन निश्चित रूप से ये दोनों implicitया तो हो सकते हैं explicit

यह भी ध्यान दें कि कोई केवल इस संरचना को किसी कास्ट के माध्यम से इनिशियलाइज़ कर सकता है string, लेकिन कोई यह परीक्षण कर सकता है कि इस तरह की कास्ट IsValid staticविधि का उपयोग करके अग्रिम में विफल होगी या नहीं ।

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

तो मेरा सवाल यह है कि आप इस पैटर्न का उपयोग करने के फायदे और नुकसान के रूप में क्या देखते हैं, और क्यों?

यदि आपको लगता है कि यह एक बुरा पैटर्न है, तो मैं यह समझना चाहूंगा कि क्यों और क्या आपको लगता है कि यह सबसे अच्छा विकल्प है।

नायब मैं मूल रूप से कहा कि स्टैक ओवरफ़्लो पर इस सवाल का , लेकिन यह मुख्य रूप से राय-आधारित (विडंबना यह है कि अपने आप में व्यक्तिपरक) के रूप में रोक दिया गया - यहाँ उम्मीद है कि यह आनंद ले सकता है और अधिक सफलता।

इन सबसे ऊपर, मूल पाठ, कुछ और विचारों से नीचे है आंशिक रूप से जवाब के जवाब में वहाँ प्राप्त होने से पहले ही पकड़ आगे कहा:

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

आप एक टेम्प्लेटेड संस्करण बना सकते हैं जो एक बूल (टी) लैम्ब्डा लेता है
शाफ़्ट फ्रीक

जवाबों:


4

यह मानक एमएल / OCaml / F # / हास्केल जैसी एमएल-शैली भाषाओं में काफी आम है, जहां रैपिड प्रकार बनाने में बहुत आसान है। यह आपको दो लाभ प्रदान करता है:

  • यह कोड के एक टुकड़े को लागू करने की अनुमति देता है कि एक स्ट्रिंग को मान्यता से गुजरना पड़ता है, बिना उस मान्यता का ध्यान रखे।
  • यह आपको एक ही स्थान पर सत्यापन कोड स्थानीय बनाना अनुमति देता है। यदि ValidatedNameकभी कोई अमान्य मान होता है, तो आप जानते हैं कि त्रुटि IsValidविधि में है।

यदि आपको IsValidविधि सही मिलती है , तो आपके पास गारंटी है कि कोई भी फ़ंक्शन जो ValidatedNameवास्तव में है, एक मान्य नाम प्राप्त कर रहा है।

यदि आपको स्ट्रिंग जोड़तोड़ करने की आवश्यकता है, तो आप एक सार्वजनिक विधि जोड़ सकते हैं जो एक फ़ंक्शन को स्वीकार करता है जो स्ट्रिंग (मान ValidatedName) लेता है और एक स्ट्रिंग (नया मान) देता है और फ़ंक्शन को लागू करने के परिणाम को मान्य करता है। यह अंतर्निहित स्ट्रिंग मूल्य प्राप्त करने और इसे फिर से लपेटने के बॉयलरप्लेट को समाप्त करता है।

रैपिंग वैल्यू के लिए एक संबंधित उपयोग उनके सिद्धता को ट्रैक करना है। Eg C- आधारित OS API कभी-कभी पूर्णांकों के रूप में संसाधनों के लिए हैंडल देते हैं। आप इसके बजाय एक Handleसंरचना का उपयोग करने के लिए ओएस एपीआई को लपेट सकते हैं और केवल कोड के उस हिस्से के लिए निर्माता को पहुंच प्रदान कर सकते हैं। यदि कोड जो Handles उत्पन्न करता है वह सही है, तो केवल मान्य हैंडल का उपयोग किया जाएगा।


1

आप इस पैटर्न का उपयोग करने के फायदे और नुकसान के रूप में क्या देखते हैं, और क्यों?

अच्छा :

  • यह स्वयं निहित है। बहुत से सत्यापन बिट्स में विभिन्न स्थानों पर पहुंचने वाले टेंडरिल होते हैं।
  • यह स्व-प्रलेखन में मदद करता है। एक विधि को देखने ValidatedStringसे कॉल के शब्दार्थ के बारे में बहुत कुछ स्पष्ट हो जाता है।
  • यह सार्वजनिक विधियों में दोहराए जाने की आवश्यकता के बजाय सत्यापन को एक स्थान पर सीमित करने में मदद करता है।

खराब :

  • कास्टिंग ट्रिक छिपी हुई है। यह मुहावरेदार सी # नहीं है, इसलिए कोड पढ़ते समय भ्रम पैदा हो सकता है।
  • यह फेंकता है। वे तार जो सत्यापन को पूरा नहीं करते हैं, एक असाधारण परिदृश्य नहीं है। IsValidकलाकारों से पहले करना थोड़ा नासमझी है।
  • यह आपको नहीं बता सकता कि कुछ अमान्य क्यों है।
  • डिफ़ॉल्ट ValidatedStringमान्य / मान्य नहीं है।

मैंने इस तरह की चीजों को अधिक बार Userऔर AuthenticatedUserचीजों के प्रकार के साथ देखा है , जहां वस्तु वास्तव में बदलती है। यह एक अच्छा दृष्टिकोण हो सकता है, हालांकि यह सी # में जगह से बाहर लगता है।


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

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

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

@ डोभाल - मैं आमतौर पर सहमत हूं। अवधारणा ही ठीक है, लेकिन यह प्रभावी रूप से एक भाषा है कि उन्हें समर्थन नहीं करता है में शोधन प्रकार जूता पहनने का साधन कोशिश कर रहा है। हमेशा कार्यान्वयन के मुद्दे होने जा रहे हैं।
Telastyn

यह कहने के बाद, मुझे लगता है कि आप "आउटबाउंड" कलाकारों पर डिफ़ॉल्ट मान के लिए जाँच कर सकते हैं और संरचना के तरीकों के भीतर किसी भी अन्य आवश्यक स्थान पर और यदि आरंभीकरण नहीं किया जाता है, लेकिन वह गड़बड़ होने लगता है।
gmoody1979

0

आपका रास्ता काफी भारी और गहन है। मैं आमतौर पर डोमेन संस्थाओं को परिभाषित करता हूं जैसे:

public class Institution
{
    private Institution() { }

    public Institution(int organizationId, string name)
    {
        OrganizationId = organizationId;            
        Name = name;
        ReplicationKey = Guid.NewGuid();

        new InstitutionValidator().ValidateAndThrow(this);
    }

    public int Id { get; private set; }
    public string Name { get; private set; }        
    public virtual ICollection<Department> Departments { get; private set; }

    ... other properties    

    public Department AddDepartment(string name)
    {
        var department = new Department(Id, name);
        if (Departments == null) Departments = new List<Department>();
        Departments.Add(department);            
        return department;
    }

    ... other domain operations
}

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

इस इकाई का सत्यापन एक अलग वर्ग है:

public class InstitutionValidator : AbstractValidator<Institution>
{
    public InstitutionValidator()
    {
        RuleFor(institution => institution.Name).NotNull().Length(1, 100).WithLocalizedName(() =>   Prim.Mgp.Infrastructure.Resources.GlobalResources.InstitutionName);       
        RuleFor(institution => institution.OrganizationId).GreaterThan(0);
        RuleFor(institution => institution.ReplicationKey).NotNull().NotEqual(Guid.Empty);
    }  
}

इन सत्यापनकर्ताओं का आसानी से पुन: उपयोग किया जा सकता है, और आप कम बॉयलरप्लेट कोड लिखते हैं। और एक और फायदा यह है कि यह पठनीय है।


क्या डाउनवॉटर यह बताने के लिए परवाह करेगा कि मेरा उत्तर क्यों अस्वीकृत किया गया?
एल-फोर

प्रश्न विवश मूल्य प्रकार के एक struct के बारे में था, और तुम क्यों समझा बिना एक वर्ग में स्विच। (एक
नीच व्यक्ति

मैंने समझाया कि मुझे यह एक बेहतर विकल्प क्यों लगता है, और यह उनके सवालों में से एक था। उत्तर के लिए धन्यवाद।
एल-फोर

0

मुझे मूल्य प्रकारों के लिए यह दृष्टिकोण पसंद है। अवधारणा महान है, लेकिन मेरे पास कार्यान्वयन के बारे में कुछ सुझाव / शिकायतें हैं।

कास्टिंग : मुझे इस मामले में कास्टिंग का उपयोग पसंद नहीं है। स्पष्ट स्ट्रिंग कलाकारों से कोई समस्या नहीं है, लेकिन (ValidatedName)nameValueनए और नए के बीच बहुत अंतर नहीं है ValidatedName(nameValue)। तो यह एक तरह से अनावश्यक लगता है। स्ट्रींग कास्ट के लिए निहित सबसे खराब समस्या है। मुझे लगता है कि वास्तविक स्ट्रिंग मूल्य प्राप्त करना अधिक स्पष्ट होना चाहिए, क्योंकि यह गलती से स्ट्रिंग को सौंपा जा सकता है और संकलक आपको संभावित "सटीक नुकसान" के बारे में चेतावनी नहीं देगा। इस तरह का सटीक नुकसान स्पष्ट होना चाहिए।

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

बराबरी और GetHashCode : संरचनाएं डिफ़ॉल्ट रूप से संरचनात्मक समानता का उपयोग कर रही हैं। तो आपके Equalsऔर GetHashCodeइस डिफ़ॉल्ट व्यवहार की नकल कर रहे हैं। आप उन्हें हटा सकते हैं और यह बहुत ज्यादा एक ही बात होगी।


कास्टिंग: शब्दशः यह मेरे लिए एक नए मान्यकृत नाम के निर्माण के बजाय एक स्ट्रिंग को एक ValidatedName में बदलने जैसा लगता है: हम एक मौजूदा स्ट्रिंग की पहचान एक मान्य नाम के रूप में कर रहे हैं। इसलिए मेरे लिए कास्ट शब्दार्थ से अधिक सही लगता है। सहमत टाइपिंग में बहुत कम अंतर है (कीबोर्ड विविधता पर उंगलियों का)। मैं टू-स्ट्रिंग कास्ट से असहमत हूं:
मान्यनाम

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

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

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

क्षमा करें, बराबरी पर अच्छी बात है। हालांकि ओवरराइड को बेहतर प्रदर्शन करना चाहिए, क्योंकि तुलना करने के लिए डिफ़ॉल्ट व्यवहार को प्रतिबिंब का उपयोग करने की आवश्यकता होती है। कास्टिंग: हाँ, संभवतः इसे एक स्पष्ट कलाकार बनाने के लिए एक अच्छा तर्क।
gmoody1979
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.