किस बिंदु पर अपरिवर्तनीय कक्षाएं बोझ बन जाती हैं?


16

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

उदाहरण के लिए, यहाँ एक नामांकित चीज़ का प्रतिनिधित्व करने के लिए एक अपरिवर्तनीय वर्ग है (मैं C # सिंटैक्स का उपयोग कर रहा हूं लेकिन सिद्धांत सभी OO भाषाओं पर लागू होता है)

class NamedThing
{
    private string _name;    
    public NamedThing(string name)
    {
        _name = name;
    }    
    public NamedThing(NamedThing other)
    {
         this._name = other._name;
    }
    public string Name
    {
        get { return _name; }
    }
}

नामित चीजों का निर्माण, नामांकित और नई नामित चीजों के लिए कॉपी किया जा सकता है लेकिन नाम नहीं बदला जा सकता है।

यह सब अच्छा है लेकिन क्या होता है जब मैं एक और विशेषता जोड़ना चाहता हूं? मुझे कंस्ट्रक्टर में एक पैरामीटर जोड़ना होगा और कॉपी कंस्ट्रक्टर को अपडेट करना होगा; जो बहुत ज्यादा काम नहीं है, लेकिन समस्याएं शुरू होती हैं, जहां तक ​​मैं देख सकता हूं, जब मैं एक जटिल वस्तु को अपरिवर्तनीय बनाना चाहता हूं ।

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

तो किस बिंदु पर एक वर्ग अपरिवर्तनीय होने के लिए बहुत जटिल हो जाता है?


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

1
आप इस प्रश्न में सुझाए गए बिल्डर पैटर्न को देख सकते हैं: stackoverflow.com/questions/1304154/…
Ant

क्या आपने मेम्बरवाइज़लोन को देखा है? आपको प्रत्येक नए सदस्य के लिए प्रतिलिपि निर्माता को अपडेट नहीं करना होगा।
केविन क्लाइन

3
@ यदि आपके संग्रह और उनके पास मौजूद सभी चीजें भी अपरिवर्तनीय हैं, तो आपको गहरी प्रति की आवश्यकता नहीं है, उथली प्रतिलिपि पर्याप्त है।
mjcopple

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

जवाबों:


23

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

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

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

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

इंजीनियरिंग समझौता और ट्रेड-ऑफ के माध्यम से मॉडलिंग का कार्य है। सब कुछ संपादन द्वारा अपरिवर्तनीय बनाना क्योंकि किसी ने पढ़ा कि सब कुछ एक्स या वाई कार्यात्मक भाषा (एक पूरी तरह से अलग प्रोग्रामिंग मॉडल) में अपरिवर्तनीय है, जो स्वीकार्य नहीं है। यह अच्छी इंजीनियरिंग नहीं है।

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

अपरिवर्तनीयता वास्तव में तेजी से एक बोझ बन जाती है (यह आकस्मिक जटिलता में जोड़ता है) यदि यह बिना किसी कारण के किया जाता है, जब इसे एक डोमेन मॉडल के संदर्भ में समझ में आता है।

मैं, एक के लिए, इससे बचने की कोशिश करता हूं (जब तक कि मैं एक प्रोग्रामिंग भाषा में काम नहीं कर रहा हूं, इसके लिए अच्छा वाक्यविन्यास समर्थन है।)


6
luis, क्या आपने देखा है कि कैसे सरल, अभी तक ध्वनि इंजीनियरिंग सिद्धांतों की व्याख्या के साथ लिखे गए सुविचारित, व्यावहारिक रूप से सही उत्तर हैं, जो अत्याधुनिक कोडिंग फ़ेड्स का उपयोग करने वालों को उतने वोट नहीं मिलते हैं? यह एक शानदार, शानदार जवाब है।
Huperniketes

3
धन्यवाद :) मैंने स्वयं रेप-ट्रेंड पर गौर किया है, लेकिन यह ठीक है। Fad fanboys मंथन कोड है कि हम बाद में बेहतर प्रति घंटा की दर से मरम्मत करने के लिए, hahah :) jk ...
luis.espinal

4
चांदी की गोली? नहीं। C # / Java में थोड़ी अजीबता के लायक है (यह वास्तव में बुरा नहीं है)? पूर्ण रूप से। इसके अलावा, अपरिवर्तनीयता में मल्टीकोर की भूमिका काफी मामूली है ... वास्तविक लाभ तर्क की आसानी है।
मौरिसियो शेफ़र

@ मौरिसियो - यदि आप ऐसा कहते हैं (जावा में अपरिवर्तनीयता वह खराब नहीं है)। 1998 से 2011 तक जावा पर काम करने के बाद, मैं अलग होने की माँग करूँगा, यह सरल कोड आधारों में मामूली छूट नहीं है। हालांकि, लोगों के पास अलग-अलग अनुभव हैं, और मैं स्वीकार करता हूं कि मेरा पीओवी व्यक्तिपरकता से मुक्त नहीं है। क्षमा करें, वहाँ सहमत नहीं हो सकते। हालांकि, जब यह अपरिवर्तनीयता की बात आती है, तो मैं सबसे महत्वपूर्ण बात होने की आसानी के साथ सहमत होता हूं।
luis.espinal

13

मैं जहाँ संभव हो वहाँ कक्षाओं पर जोर देने के दौर से गुजरा। बिल्डरों के लिए बहुत अधिक सब कुछ, अपरिवर्तनीय सरणियाँ, आदि, मैंने पाया था कि आपके प्रश्न का उत्तर सरल है: किस बिंदु पर अपरिवर्तनीय कक्षाएं बोझ बन जाती हैं? बहुत जल्दी। जैसे ही आप किसी चीज़ को सीरियस करना चाहते हैं, आपको डिसेर्बलाइज़ करने में सक्षम होना होगा, जिसका अर्थ है कि यह परस्पर होना चाहिए; जैसे ही आप एक ओआरएम का उपयोग करना चाहते हैं, उनमें से अधिकांश गुणों पर निर्भर करते हैं, जो परस्पर परिवर्तनशील हैं। और इसी तरह।

मैंने अंततः उस नीति को परिवर्तनशील वस्तुओं के लिए अपरिवर्तनीय इंटरफेस के साथ बदल दिया।

class NamedThing : INamedThing
{
    private string _name;    
    public NamedThing(string name)
    {
        _name = name;
    }    

    public NamedThing(NamedThing other)
    {
        this._name = other._name;
    }

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

interface INamedThing
{
    string Name { get; }
}

अब ऑब्जेक्ट में लचीलापन है लेकिन आप अभी भी कॉलिंग कोड बता सकते हैं कि यह इन गुणों को संपादित नहीं करना चाहिए।


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

1
@ सार - दिलचस्प बिंदु। मुझे लगता है कि यह एक वास्तविक सुरक्षा से अधिक एक मनोवैज्ञानिक चीज है। हालाँकि, आपकी अंतिम पंक्ति का गलत आधार है। इसे अलग-थलग रखने का कारण यह है कि विभिन्न चीजों को इसे तात्कालिक बनाने और प्रतिबिंब के माध्यम से आबाद करने की आवश्यकता है, न कि एक बार तात्कालिक रूप से परिवर्तित करने के लिए।
पीडीआर

1
@ चेतावनी: उसी तरह IComparable<T>गारंटी देता है कि अगर X.CompareTo(Y)>0और Y.CompareTo(Z)>0, तब X.CompareTo(Z)>0इंटरफेसेस का अनुबंध है । यदि अनुबंध IImmutableList<T>यह निर्दिष्ट करता है कि सभी वस्तुओं और गुणों के मूल्यों को बाहरी दुनिया में उजागर होने से पहले "पत्थर में सेट" होना चाहिए, तो सभी वैध कार्यान्वयन ऐसा करेंगे। कुछ भी IComparableकार्यान्वयन को सकारात्मकता के उल्लंघन से रोकता है , लेकिन कार्यान्वयन जो ऐसा करते हैं वह नाजायज हैं। अगर एक SortedDictionaryनाजायज बात बताई जाती है IComparable, तो ...
सुपरकैट

1
@Aaronaught: मैं क्यों विश्वास करना चाहिए कि के कार्यान्वयन IReadOnlyList<T>अपरिवर्तनीय हो जाएगा, यह देखते हुए कि (1) ऐसी कोई आवश्यकता इंटरफेस दस्तावेज में (2) सबसे आम कार्यान्वयन कहा गया है, और, List<T>, केवल पढ़ने के लिए भी नहीं है ? मैं बिल्कुल स्पष्ट नहीं हूं कि मेरी शर्तों के बारे में क्या अस्पष्ट है: एक संग्रह पठनीय है यदि भीतर डेटा पढ़ा जा सकता है। यह केवल तभी पढ़ा जाता है जब यह वादा कर सकता है कि इसमें निहित डेटा को तब तक नहीं बदला जा सकता जब तक कि कुछ बाहरी संदर्भ कोड द्वारा आयोजित न किए जाएं जो इसे बदल देगा। यह अपरिवर्तनीय है अगर यह गारंटी दे सकता है कि इसे बदला नहीं जा सकता है, अवधि।
सुपरकैट

1
@supercat: संयोग से, Microsoft मुझसे सहमत है। उन्होंने एक अपरिवर्तनीय संग्रह पैकेज जारी किया और ध्यान दिया कि वे सभी ठोस प्रकार हैं, क्योंकि आप कभी भी गारंटी नहीं दे सकते कि अमूर्त वर्ग या इंटरफ़ेस वास्तव में अपरिवर्तनीय है।
Aaronaught

8

मुझे नहीं लगता कि इसका कोई सामान्य उत्तर है। एक वर्ग जितना अधिक जटिल होता है, वह अपने राज्य परिवर्तनों के बारे में तर्क करना उतना ही कठिन होता है, और यह महंगा होता है कि इसकी नई प्रतियां बनाई जाएं। तो जटिलता के कुछ (व्यक्तिगत) स्तर से ऊपर यह एक वर्ग को अपरिवर्तनीय बनाने / रखने के लिए बहुत दर्दनाक हो जाएगा।

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

तो आमतौर पर पसंदीदा समाधान इस तरह के एक वर्ग को कई अलग-अलग वर्गों में विभाजित करने के लिए होगा, जिनमें से प्रत्येक को अपने आप पर परस्पर या अपरिवर्तनीय बनाया जा सकता है। यदि यह संभव नहीं है, तो इसे बदल दिया जा सकता है।


5

यदि आप अपने सभी अपरिवर्तनीय क्षेत्रों को एक आंतरिक में संग्रहीत करते हैं तो आप कॉपी की समस्या से बच सकते हैं struct। यह मूल रूप से स्मृति चिन्ह पैटर्न की एक भिन्नता है। फिर जब आप एक प्रतिलिपि बनाना चाहते हैं, तो स्मृति चिन्ह की प्रतिलिपि बनाएँ:

class MyClass
{
    struct Memento
    {
        public int field1;
        public string field2;
    }

    private readonly Memento memento;

    public MyClass(int field1, string field2)
    {
        this.memento = new Memento()
            {
                field1 = field1,
                field2 = field2
            };
    }

    private MyClass(Memento memento) // for copying
    {
        this.memento = memento;
    }

    public int Field1 { get { return this.memento.field1; } }
    public string Field2 { get { return this.memento.field2; } }

    public MyClass WithNewField1(int newField1)
    {
        Memento newMemento = this.memento;
        newMemento.field1 = newField1;
        return new MyClass(newMemento);
    }
}

मुझे लगता है कि आंतरिक संरचना आवश्यक नहीं है। यह सिर्फ सदस्य का एक और तरीका है।
कोडिज्म

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

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

... यह परिवर्तनशील डेटा धारकों के साथ बहुत उपयोगी हो सकता है। दृष्टिकोण भी "समानांतर" उत्परिवर्तनीय और अपरिवर्तनीय वर्गों (एक अमूर्त "पठनीय" आधार से प्राप्त) के लिए बहुत आसान बनाता है और उनके निर्माता एक दूसरे से डेटा कॉपी करने में सक्षम हैं।
सुपरकैट

3

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

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

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

myList = lists:append([[1,2,3], [4,5,6]])
% myList is now [1,2,3,4,5,6]

यह अधिक जटिल वस्तुओं के साथ काम करने का एक समझदार तरीका हो सकता है। जैसा कि आप उदाहरण के लिए एक पेड़ का नोड जोड़ते हैं, परिणाम एक नया पेड़ है जो नोड के साथ जोड़ा गया है। उपरोक्त उदाहरण में विधि एक नई सूची देती है। इस पैराग्राफ में उदाहरण में tree.add(newNode)जोड़ा नोड के साथ एक नया पेड़ लौटाएगा। उपयोगकर्ताओं के लिए, इसके साथ काम करना आसान हो जाता है। पुस्तकालय लेखकों के लिए यह कठिन हो जाता है जब भाषा अंतर्निहित नकल का समर्थन नहीं करती है। वह दहलीज आपके अपने धैर्य तक है। आपकी लाइब्रेरी के उपयोगकर्ताओं के लिए, मैंने जो सबसे अधिक खोज सीमा तय की है, वह लगभग तीन से चार मापदंडों में सबसे ऊपर है।


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

0

यदि आपके पास कई अंतिम वर्ग सदस्य हैं और वे नहीं चाहते हैं कि वे उन सभी वस्तुओं के संपर्क में आएं जिन्हें इसे बनाने की आवश्यकता है, तो आप बिल्डर पैटर्न का उपयोग कर सकते हैं:

class NamedThing
{
    private string _name;    
    private string _value;
    private NamedThing(string name, string value)
    {
        _name = name;
        _value = value;
    }    
    public NamedThing(NamedThing other)
    {
        this._name = other._name;
        this._value = other._value;
    }
    public string Name
    {
        get { return _name; }
    }

    public static class Builder {
        string _name;
        string _value;

        public void setValue(string value) {
            _value = value;
        }
        public void setName(string name) {
            _name = name;
        }
        public NamedThing newObject() {
            return new NamedThing(_name, _value);
        }
    }
}

लाभ यह है कि आप एक अलग नाम के केवल एक अलग मूल्य के साथ आसानी से एक नई वस्तु बना सकते हैं।


मुझे लगता है कि आपका बिल्डर स्थिर होना सही नहीं है। आपके द्वारा सेट किए जाने के बाद एक और धागा स्थैतिक नाम या स्थिर मूल्य बदल सकता है, लेकिन इससे पहले कि आप कॉल करें newObject
ErikE

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

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

1
जावा में #Salandur इनर क्लास हमेशा "स्थिर" होते हैं।
सेबस्टियन रेडल

0

तो किस बिंदु पर एक वर्ग अपरिवर्तनीय होने के लिए बहुत जटिल हो जाता है?

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

लगातार डेटा संरचनाएं

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

सस्ते कॉपियां

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

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

बोझ

बोझ के लिए, मेरे मामले में कम से कम एक तत्काल है। मुझे उन बिल्डरों की आवश्यकता है जिनके बारे में लोग या "ग्राहक" बात कर रहे हैं क्योंकि मैं उन्हें बिना किसी स्पर्श के उस बड़े पैमाने पर डेटा संरचना में प्रभावी ढंग से परिवर्तन व्यक्त करने में सक्षम होने के लिए कहता हूं। इस तरह कोड:

void transform_stuff(MutList<Stuff>& stuff, int first, int last)
{
     // Transform stuff in the range, [first, last).
     for (; first != last; ++first)
          transform(stuff[first]);
}

... फिर इस तरह लिखना होगा:

ImmList<Stuff> transform_stuff(ImmList<Stuff> stuff, int first, int last)
{
     // Grab a "transient" (builder) list we can modify:
     TransientList<Stuff> transient(stuff);

     // Transform stuff in the range, [first, last)
     // for the transient list.
     for (; first != last; ++first)
          transform(transient[first]);

     // Commit the modifications to get and return a new
     // immutable list.
     return stuff.commit(transient);
}

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

अपवाद-सुरक्षा या त्रुटि पुनर्प्राप्ति

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

यदि आप मूल म्यूटिंग फ़ंक्शन को अपवाद-सुरक्षित बनाना चाहते हैं, तो उसे रोलबैक तर्क की आवश्यकता है, जिसके लिए सरलतम कार्यान्वयन के लिए संपूर्ण प्रतिलिपि की आवश्यकता होती है सूची :

void transform_stuff(MutList<Stuff>& stuff, int first, int last)
{
    // Make a copy of the whole massive gigabyte-sized list 
    // in case we encounter an exception and need to rollback
    // changes.
    MutList<Stuff> old_stuff = stuff;

    try
    {
         // Transform stuff in the range, [first, last).
         for (; first != last; ++first)
             transform(stuff[first]);
    }
    catch (...)
    {
         // If the operation failed and ran into an exception,
         // swap the original list with the one we modified
         // to "undo" our changes.
         stuff.swap(old_stuff);
         throw;
    }
}

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

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

आपको किसी फ़ंक्शन में बैक साइड इफेक्ट्स को रोल करने के बारे में चिंता करने की ज़रूरत नहीं है जो किसी भी कारण नहीं है!

तो मूल प्रश्न पर वापस जाएं:

किस बिंदु पर अपरिवर्तनीय कक्षाएं बोझ बन जाती हैं?

वे हमेशा उन भाषाओं में बोझ होते हैं जो अपरिवर्तनीयता की तुलना में अधिक परिवर्तनशीलता के आसपास घूमती हैं, यही कारण है कि मुझे लगता है कि आपको उनका उपयोग करना चाहिए जहां लाभ लागत को काफी कम कर देते हैं। लेकिन बड़े पर्याप्त डेटा संरचनाओं के लिए व्यापक स्तर पर, मेरा मानना ​​है कि ऐसे कई मामले हैं जहां यह एक योग्य व्यापार-बंद है।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.