यदि अपरिवर्तनीय वस्तुएं अच्छी हैं, तो लोग उत्परिवर्तित वस्तुएं क्यों बनाते रहते हैं? [बन्द है]


250

यदि अपरिवर्तनीय वस्तुएं अच्छी हैं, सरल हैं और समवर्ती प्रोग्रामिंग में लाभ प्रदान करते हैं तो प्रोग्रामर क्यों उत्परिवर्तनीय वस्तुएं बनाते रहते हैं?

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


¹ अपरिवर्तनीय वस्तु एक ऐसी वस्तु है जिसकी स्थिति निर्मित होने के बाद संशोधित नहीं की जा सकती है।
² उत्परिवर्तित वस्तु एक ऐसी वस्तु है जिसे बनने के बाद संशोधित किया जा सकता है।


42
मुझे लगता है कि, वैध कारणों के अलावा (जैसा कि नीचे Péter द्वारा उल्लेख किया गया है), "आलसी डेवलपर" "बेवकूफ" डेवलपर की तुलना में एक अधिक सामान्य कारण है। और "बेवकूफ डेवलपर" से पहले "अनजाने डेवलपर" भी हैं।
जोआचिम बाउर

201
हर इंजील प्रोग्रामर / ब्लॉगर के लिए 1000 एविड ब्लॉग पाठक हैं जो तुरंत खुद को फिर से आविष्कार करते हैं और नवीनतम तकनीकों को अपनाते हैं। उन लोगों में से हर एक के लिए 10,000 प्रोग्रामर होते हैं, जिनकी नाक के साथ ग्राइंड स्टोन होता है जो एक दिन काम करते हैं और दरवाजे से उत्पाद निकालते हैं। वे लोग आजमाई हुई और विश्वसनीय तकनीकों का उपयोग कर रहे हैं जो उनके लिए सालों से काम कर रही हैं। वे इंतजार करते हैं जब तक कि नई तकनीकों को व्यापक रूप से नहीं अपनाया जाता है और उन्हें लेने से पहले वास्तविक लाभ दिखाते हैं। उन्हें बेवकूफ मत कहो, और वे कुछ भी लेकिन आलसी हैं, उन्हें बदले में "व्यस्त" कहें।
बाइनरी वॉरियर

22
@BinaryWorrier: अपरिवर्तनीय वस्तुएं शायद ही एक "नई चीज" हैं। वे डोमेन ऑब्जेक्ट्स के लिए भारी उपयोग नहीं किए जा सकते थे, लेकिन जावा और सी # ने उन्हें बहुत शुरुआत से दिया था। इसके अलावा: "आलसी" हमेशा एक बुरा शब्द नहीं है, "आलसी" के कुछ प्रकार एक डेवलपर के लिए एक पूर्ण लाभ हैं।
जोकिम सॉयर

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

116
-1, अपरिवर्तनीय वस्तुएँ 'अच्छा' होती हैं। विशिष्ट स्थितियों के लिए कम या ज्यादा उपयुक्त। कोई भी आपको एक तकनीक या दूसरे को बता रहा है वस्तुतः सभी स्थितियों के लिए दूसरे पर 'अच्छा' या 'बुरा' है, आपको धर्म बेच रहा है।
ग्रैंडमास्टरबी

जवाबों:


326

दोनों उत्परिवर्तनीय और अपरिवर्तनीय वस्तुओं के अपने उपयोग, पेशेवरों और विपक्ष हैं।

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

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

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

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


27
ये सही है। विशेष रूप से जीयूआई प्रोग्रामिंग में, उत्परिवर्तनीय वस्तु बहुत काम आती है।
फ्लोरियन सालिहोविक

23
यह सिर्फ प्रतिरोध नहीं है, मुझे यकीन है कि बहुत सारे देवता नवीनतम और महानतम प्रयास करना पसंद करेंगे, लेकिन औसत देव के वातावरण में कितनी बार नई परियोजनाएं घूमती हैं जहां वे इन नई प्रथाओं को लागू कर सकते हैं? कभी नहीं या बस एक अप्रभावी राज्य की कोशिश करने के लिए एक शौक परियोजना लिख ​​सकते हैं।
स्टीवन एवर्स

29
इस के लिए दो छोटे caveats: (1) चल खेल चरित्र ले लो। Pointउदाहरण के लिए .NET में वर्ग अपरिवर्तनीय लेकिन नए अंक बनाने परिवर्तन के परिणाम के रूप में आसान है और इस तरह सस्ती है। एक अपरिवर्तनीय चरित्र को एनिमेट करना "चलते हुए हिस्सों" को डिकूपिंग करके बहुत सस्ता बनाया जा सकता है (लेकिन हाँ, कुछ पहलू तब परिवर्तनशील है)। (२) "बड़ी और / या जटिल वस्तुएँ" बहुत अच्छी तरह से अपरिवर्तनीय हो सकती हैं। तार अक्सर बड़े होते हैं, और आमतौर पर अपरिवर्तनीयता से लाभ होता है। मैंने एक बार जटिल ग्राफ वर्ग को अपरिवर्तनीय होने के लिए फिर से लिखा, जिससे कोड सरल और अधिक कुशल हो गया। ऐसे मामलों में, एक उत्परिवर्ती बिल्डर की कुंजी है।
कोनराड रुडोल्फ

4
@KonradRudolph, अच्छे अंक, धन्यवाद। मेरा मतलब जटिल वस्तुओं में अपरिवर्तनीयता का उपयोग करके शासन करना नहीं था, लेकिन इस तरह के वर्ग को सही ढंग से और कुशलता से लागू करना एक तुच्छ कार्य होने से बहुत दूर है, और आवश्यक अतिरिक्त प्रयास हमेशा उचित नहीं हो सकता है।
Péter Török

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

130

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


9
अधिकांश आधुनिक आईडीई में मुझे पता है, यह केवल गेटर्स उत्पन्न करने के लिए व्यावहारिक रूप से समान मात्रा में लेता है, जैसा कि गेटर्स और सेटरर्स दोनों उत्पन्न करने के लिए। हालांकि यह सच है कि जोड़ने है final, constआदि के लिए अतिरिक्त प्रयास का एक सा लेता है ... जब तक आप एक कोड टेम्पलेट :-) की स्थापना
पीटर Török

10
@ PéterTörök यह सिर्फ अतिरिक्त प्रयास नहीं है - यह तथ्य है कि आपके साथी कोडर आपको पुतले में लटकाना चाहेंगे क्योंकि वे आपके कोडिंग स्टाइल को उनके अनुभव से बहुत अलग पाते हैं। वह भी इस तरह की बात को हतोत्साहित करता है।
ओनोरियो कैटेनेशिया

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

3
@ PéterTörök एक अनिवार्य भाषा में उत्परिवर्तनीय वस्तुएं डिफ़ॉल्ट व्यवहार हैं। यदि आप केवल अपरिवर्तनीय वस्तुएं चाहते हैं, तो कार्यात्मक भाषा में स्विच करना सबसे अच्छा है।
Emory

3
@ PéterTörök यह मानना ​​थोड़ा भोलापन है कि किसी कार्यक्रम में अपरिवर्तनीयता को शामिल करने के लिए इन बसों को छोड़ने से ज्यादा कुछ नहीं है। आपको अभी भी प्रोग्राम की स्थिति को बढ़ाने के लिए एक तरीके की आवश्यकता है, और इसके लिए आपको बिल्डरों, या पॉपस्कूल अपरिवर्तनीयता, या परिवर्तनशील परदे के पीछे की आवश्यकता है, जिनमें से सभी को बस निपटाने के प्रयास में लगभग 1 बिलियन बार प्रयास करना चाहिए।
असद सईदुद्दीन

49
  1. पारस्परिकता के लिए एक जगह है। डोमेन संचालित डिज़ाइन सिद्धांत एक ठोस समझ प्रदान करते हैं कि क्या परिवर्तनशील होना चाहिए और क्या अपरिवर्तनीय होना चाहिए। यदि आप इसके बारे में सोचते हैं तो आपको एहसास होगा कि यह एक ऐसी प्रणाली की अवधारणा के लिए अव्यावहारिक है जिसमें किसी वस्तु के लिए राज्य के हर परिवर्तन को इसके विनाश और फिर से रचना की आवश्यकता होती है, और इसे संदर्भित करने वाली प्रत्येक वस्तु को। जटिल प्रणालियों के साथ यह आसानी से पूरे सिस्टम के ऑब्जेक्ट ग्राफ को पूरी तरह से मिटा और पुनर्निर्माण कर सकता है

  2. अधिकांश डेवलपर्स कुछ भी नहीं बनाते हैं जहां प्रदर्शन की आवश्यकताएं पर्याप्त रूप से महत्वपूर्ण हैं कि उन्हें संगामिति पर ध्यान केंद्रित करने की आवश्यकता होती है (या बहुत से अन्य मुद्दे जो सार्वभौमिक रूप से सूचित द्वारा अच्छा अभ्यास माना जाता है)।

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

        public class ImmutablePerson { 
    
         public ImmutablePerson(string name, ImmutableEventList eventsToAttend)
         {
              this.name = name;
              this.eventsToAttend = eventsToAttend;
         }
         private string name;
         private ImmutableEventList eventsToAttend;
    
         public string Name { get { return this.name; } }
    
         public ImmutablePerson RSVP(ImmutableEvent immutableEvent){
             // the person is RSVPing an event, thus mutating the state 
             // of the eventsToAttend.  so we need a new person with a reference
             // to the new Event
             ImmutableEvent newEvent = immutableEvent.OnRSVPReceived(this);
             ImmutableEventList newEvents = this.eventsToAttend.Add(newEvent));
             var newSelf = new ImmutablePerson(name, newEvents);
             return newSelf;
         }
        }
    
        public class ImmutableEvent { 
         public ImmutableEvent(DateTime when, ImmutablePersonList peopleAttending, ImmutablePersonList peopleNotAttending){
             this.when = when;     
             this.peopleAttending = peopleAttending;
             this.peopleNotAttending = peopleNotAttending;
         }
         private DateTime when; 
         private ImmutablePersonList peopleAttending;
         private ImmutablePersonList peopleNotAttending;
         public ImmutableEvent OnReschedule(DateTime when){
               return new ImmutableEvent(when,peopleAttending,peopleNotAttending);
         }
         //  notice that this will be an infinite loop, because everytime one counterpart
         //  of the bidirectional relationship is added, its containing object changes
         //  meaning it must re construct a different version of itself to 
         //  represent the mutated state, the other one must update its
         //  reference thereby obsoleting the reference of the first object to it, and 
         //  necessitating recursion
         public ImmutableEvent OnRSVPReceived(ImmutablePerson immutablePerson){
               if(this.peopleAttending.Contains(immutablePerson)) return this;
               ImmutablePersonList attending = this.peopleAttending.Add(immutablePerson);
               ImmutablePersonList notAttending = this.peopleNotAttending.Contains( immutablePerson ) 
                                    ? peopleNotAttending.Remove(immutablePerson)
                                    : peopleNotAttending;
               return new ImmutableEvent(when, attending, notAttending);
         }
        }
        public class ImmutablePersonList
        {
          private ImmutablePerson[] immutablePeople;
          public ImmutablePersonList(ImmutablePerson[] immutablePeople){
              this.immutablePeople = immutablePeople;
          }
          public ImmutablePersonList Add(ImmutablePerson newPerson){
              if(this.Contains(newPerson)) return this;
              ImmutablePerson[] newPeople = new ImmutablePerson[immutablePeople.Length];
              for(var i=0;i<immutablePeople.Length;i++)
                  newPeople[i] = this.immutablePeople[i];
              newPeople[immutablePeople.Length] = newPerson;
          }
          public ImmutablePersonList Remove(ImmutablePerson newPerson){
              if(immutablePeople.IndexOf(newPerson) != -1)
              ImmutablePerson[] newPeople = new ImmutablePerson[immutablePeople.Length-2];
              bool hasPassedRemoval = false;
              for(var i=0;i<immutablePeople.Length;i++)
              {
                 hasPassedRemoval = hasPassedRemoval || immutablePeople[i] == newPerson;
                 newPeople[i] = this.immutablePeople[hasPassedRemoval ? i + 1 : i];
              }
              return new ImmutablePersonList(newPeople);
          }
          public bool Contains(ImmutablePerson immutablePerson){ 
             return this.immutablePeople.IndexOf(immutablePerson) != -1;
          } 
        }
        public class ImmutableEventList
        {
          private ImmutableEvent[] immutableEvents;
          public ImmutableEventList(ImmutableEvent[] immutableEvents){
              this.immutableEvents = immutableEvents;
          }
          public ImmutableEventList Add(ImmutableEvent newEvent){
              if(this.Contains(newEvent)) return this;
              ImmutableEvent[] newEvents= new ImmutableEvent[immutableEvents.Length];
              for(var i=0;i<immutableEvents.Length;i++)
                  newEvents[i] = this.immutableEvents[i];
              newEvents[immutableEvents.Length] = newEvent;
          }
          public ImmutableEventList Remove(ImmutableEvent newEvent){
              if(immutableEvents.IndexOf(newEvent) != -1)
              ImmutableEvent[] newEvents = new ImmutableEvent[immutableEvents.Length-2];
              bool hasPassedRemoval = false;
              for(var i=0;i<immutablePeople.Length;i++)
              {
                 hasPassedRemoval = hasPassedRemoval || immutableEvents[i] == newEvent;
                 newEvents[i] = this.immutableEvents[hasPassedRemoval ? i + 1 : i];
              }
              return new ImmutableEventList(newPeople);
          }
          public bool Contains(ImmutableEvent immutableEvent){ 
             return this.immutableEvent.IndexOf(immutableEvent) != -1;
          } 
        }
    

2
@AndresF।, यदि आपके पास केवल अपरिवर्तनीय वस्तुओं का उपयोग करके द्विदिशीय रिश्तों के साथ जटिल ग्राफ़ को बनाए रखने का तरीका अलग है, तो मुझे यह सुनना अच्छा लगेगा। (मुझे लगता है कि हम इस बात से सहमत हो सकते हैं कि एक संग्रह / सरणी एक वस्तु है)
Smartcaveman

2
@AndresF।, (1) मेरा पहला बयान सार्वभौमिक नहीं था इसलिए यह गलत नहीं था। मैंने वास्तव में यह समझाने के लिए एक कोड उदाहरण प्रदान किया कि यह कुछ मामलों में कैसे जरूरी था, जो कि अनुप्रयोग विकास में बहुत आम है। (2) द्विदिश संबंधों को सामान्य रूप से पारस्परिकता की आवश्यकता होती है। मैं नहीं मानता कि जावा अपराधी है। जैसा कि मैंने कहा, मुझे आपके द्वारा सुझाए गए किसी भी रचनात्मक विकल्प का मूल्यांकन करने में खुशी होगी, लेकिन इस समय आप टिप्पणी कर रहे हैं जैसे "आप गलत हैं क्योंकि मैंने ऐसा कहा है"।
स्मार्टवॉमन

14
@smartcaveman के बारे में (2), मैं भी असहमत हूं: सामान्य तौर पर, "बिडायरेक्शनल रिलेशनशिप" एक गणितीय अवधारणा ऑर्थोगोनल है जो म्यूटेबिलिटी के लिए है। जैसा कि आमतौर पर जावा में कार्यान्वित किया जाता है, इसमें परिवर्तनशीलता की आवश्यकता होती है (मैं उस बिंदु पर आपके साथ सहमत हूं)। हालांकि, मैं एक वैकल्पिक कार्यान्वयन के बारे में सोच सकता हूं: एक निर्माणकर्ता के साथ दो वस्तुओं के बीच एक संबंध वर्ग Relationship(a, b); संबंध बनाने के बिंदु पर, दोनों इकाइयाँ aऔर bपहले से मौजूद हैं, और यह संबंध स्वयं भी अपरिवर्तनीय है। मैं यह नहीं कह रहा हूं कि यह दृष्टिकोण जावा में व्यावहारिक है; बस यह संभव है।
एंड्रेस एफ।

4
@AndresF।, तो, आप क्या कह रहे हैं, तो के आधार पर Rहै Relationship(a,b)और दोनों aऔर bअपरिवर्तनीय हैं, न तो aहै और न ही bके लिए एक संदर्भ पकड़ होगा R। इस काम के लिए, संदर्भ को कहीं और (स्थिर वर्ग की तरह) संग्रहीत करना होगा। क्या मैं आपका इरादा सही तरीके से समझ रहा हूँ?
स्मार्टवॉमन

9
अपरिवर्तनीय डेटा के लिए एक द्विदिश संबंध को संग्रहीत करना संभव है क्योंकि चुतुलु आलस्य के माध्यम से बताते हैं। यहाँ ऐसा करने का एक तरीका है: haskell.org/haskellwiki/Tying_the_Knot
थॉमस एडिंग

36

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

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

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


3
क्या यह ओकासाकी किताब है?
मैकेनिकल घोंघा

हां। थोड़े सूखे लेकिन अच्छी जानकारी के एक टन ...
पॉल Sanwald

4
मजेदार, मैंने हमेशा सोचा था कि ओकासकिस लाल / काला पेड़ इतना सरल था। 10 लाइनों या तो। मुझे लगता है कि सबसे बड़ा फायदा यह है कि जब आप वास्तव में पुराने संस्करण को भी अपने पास रखना चाहते हैं।
थॉमस अहले

जबकि अंतिम वाक्य शायद अतीत में सच हो गया है, यह स्पष्ट नहीं है कि यह भविष्य में सही रहेगा, वर्तमान हार्डवेयर रुझान आदि
jk।

29

मेरे दृष्टिकोण से, यह जागरूकता की कमी है। यदि आप अन्य ज्ञात JVM भाषाओं (Scala, Clojure) को देखते हैं, तो उत्परिवर्तनीय वस्तुएं कोड में शायद ही कभी देखी जाती हैं और इसीलिए लोग उन परिदृश्यों में उनका उपयोग करना शुरू करते हैं जहां एकल थ्रेडिंग पर्याप्त नहीं है।

मैं वर्तमान में क्लोजर सीख रहा हूं और स्काला (4 साल + के साथ-साथ जावा) में थोड़ा अनुभव है और राज्य की जागरूकता के कारण आपकी कोडिंग शैली बदलती है।


शायद "लोकप्रिय" के बजाय "ज्ञात" शब्द का एक बेहतर विकल्प होगा।
डेन

हाँ य़ह सही हैं।
फ्लोरियन सालिहोविक

5
+1: मैं सहमत हूं: कुछ स्काला और हास्केल सीखने के बाद, मैं हर जगह जावा और कास्ट में अंतिम उपयोग करता हूं। यदि संभव हो तो मैं अपरिवर्तनीय वस्तुओं का भी उपयोग करता हूं और जबकि उत्परिवर्तित वस्तुओं की अभी भी बहुत आवश्यकता है, यह आश्चर्यजनक है कि आप कितनी बार अपरिवर्तनीय वस्तुओं का उपयोग कर सकते हैं।
जियोर्जियो

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

13

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


4
+1, गेट्टर / सेटर पैटर्न का उपयोग वैसे भी किया जाता है जैसा कि पहले डेटा विश्लेषण के बाद किसी प्रकार के डिफ़ॉल्ट कार्यान्वयन में होता है।
जाप

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

12

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

स्पष्ट रूप से उत्परिवर्तित वस्तुएं हाइबरनेट की तुलना में बहुत लंबे समय तक रही हैं, इसलिए हाइबरनेट संभवतः उत्परिवर्तित वस्तुओं की लोकप्रियता का मूल 'कारण' नहीं है। शायद उत्परिवर्तित वस्तुओं की लोकप्रियता ने हाइबरनेट को पनपने दिया।

लेकिन आज अगर कई जूनियर प्रोग्रामर हाइबरनेट या किसी अन्य ORM का उपयोग करके एंटरप्राइज सिस्टम पर अपने दांत काटते हैं, तो संभवतः वे उत्परिवर्तित वस्तुओं का उपयोग करने की आदत डाल लेंगे। Hibernate जैसी चौखटे, उत्परिवर्तनीय वस्तुओं की लोकप्रियता को बढ़ा सकती है।


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

9

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

कई कार्यक्रमों को वास्तविक दुनिया की चीजों को मॉडल करने के लिए डिज़ाइन किया गया है जो स्वाभाविक रूप से उत्परिवर्तनीय हैं। मान लीजिए कि 12:51 बजे, कुछ चर AllTrucksऑब्जेक्ट # 451 का संदर्भ रखता है, जो एक डेटा संरचना का मूल है, जो इंगित करता है कि उस क्षण में बेड़े के सभी ट्रकों में क्या कार्गो निहित है (12:51 am), और कुछ चर BobsTruckएक वस्तु के लिए # 24601 अंक वस्तु का संदर्भ प्राप्त करने के लिए इस्तेमाल किया जा सकता है जो इंगित करता है कि बॉब के ट्रक में उस समय (12:51 बजे) क्या कार्गो निहित है। 12:52 बजे, कुछ ट्रक (बॉब सहित) लोड और अनलोड किए जाते हैं, और डेटा संरचनाएं अपडेट की जाती हैं, ताकि AllTrucksअब एक डेटा संरचना का संदर्भ होगा जो इंगित करता है कि कार्गो सभी ट्रकों में 12:52 बजे तक है।

क्या होना चाहिए BobsTruck?

यदि प्रत्येक ट्रक वस्तु की 'कार्गो' संपत्ति अपरिवर्तनीय है, तो ऑब्जेक्ट # 24601 हमेशा के लिए उस स्थिति का प्रतिनिधित्व करेगा जो बॉब का ट्रक 12:51 बजे था। यदि BobsTruckऑब्जेक्ट का सीधा संदर्भ # 24601 है, तो जब तक कि कोड जो अपडेट करने के लिए AllTrucksभी अपडेट नहीं होता है BobsTruck, वह बॉब के ट्रक की वर्तमान स्थिति का प्रतिनिधित्व करने के लिए बंद हो जाएगा। आगे ध्यान दें कि जब तक कि BobsTruckउत्परिवर्तित वस्तु के किसी रूप में संग्रहित नहीं किया जाता है, केवल एक ही तरीका है कि जो कोड अद्यतन AllTrucksकर सकता है वह अद्यतन करेगा यदि कोड स्पष्ट रूप से ऐसा करने के लिए प्रोग्राम किया गया था।

यदि कोई व्यक्ति BobsTruckसभी वस्तुओं को अपरिवर्तनीय रखते हुए राज्य बॉब के ट्रक का निरीक्षण करने में सक्षम होना चाहता है , तो BobsTruckवह एक अपरिवर्तनीय कार्य हो सकता है , जो AllTrucksकिसी विशेष समय पर होने वाले मूल्य को देखते हुए , बॉब के ट्रक की स्थिति का उत्पादन करेगा। उस समय। यहां तक ​​कि यह अपरिवर्तनीय कार्यों की एक जोड़ी भी पकड़ सकता है - जिनमें से एक उपरोक्त के रूप में होगा, और जिनमें से दूसरा एक बेड़े राज्य और एक नए ट्रक राज्य के संदर्भ को स्वीकार करेगा, और एक नए बेड़े राज्य के लिए एक संदर्भ लौटाएगा जो पुराने से मेल खाता है, सिवाय इसके कि बॉब के ट्रक में नया राज्य होगा।

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

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

यदि अलग-अलग ट्रक ऑब्जेक्ट परस्पर भिन्न हैं , लेकिन अपरिवर्तनीय पहचान हैं , तो बहु-थ्रेडेड परिदृश्य क्लीनर बन जाता है। किसी भी ट्रक पर एक समय में केवल एक ही धागे को चलाने की अनुमति दी जा सकती है, लेकिन विभिन्न ट्रकों पर चलने वाले धागे बिना किसी हस्तक्षेप के ऐसा कर सकते हैं। हालांकि ऐसे तरीके हैं जो अपरिवर्तनीय वस्तुओं का उपयोग करते समय भी इस तरह के व्यवहार का अनुकरण कर सकते हैं (जैसे कोई "AllTrucks" ऑब्जेक्ट्स को परिभाषित कर सकता है ताकि XXX से SSS तक ट्रक की स्थिति को स्थापित करने के लिए बस एक वस्तु उत्पन्न करने की आवश्यकता होगी जो कहा "जैसा कि [समय], आदि [XXX] से संबंधित ट्रक का राज्य अब [SSS] है, बाकी सब चीज़ों की स्थिति [AllTrucks का पुराना मूल्य है] "। ऐसी वस्तु उत्पन्न करना इतना तेज़ होगा कि विवाद की उपस्थिति में भी, एCompareExchangeपाश लंबे समय तक नहीं लगेगा। दूसरी ओर, इस तरह के डेटा संरचना का उपयोग करने से किसी विशेष व्यक्ति के ट्रक को खोजने के लिए आवश्यक समय की मात्रा में वृद्धि होगी। अपरिवर्तनीय पहचान वाली परिवर्तनशील वस्तुओं का उपयोग करने से उस समस्या से बचा जाता है।


8

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

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


7

इस ब्लॉग पोस्ट की जाँच करें: http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html । यह संक्षेप में बताता है कि अपरिवर्तनीय वस्तुएं उत्परिवर्तनीय से बेहतर क्यों हैं। यहाँ तर्क की एक छोटी सूची है:

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

जहां तक ​​मुझे समझ में आया है, लोग उत्परिवर्तनीय वस्तुओं का उपयोग कर रहे हैं, क्योंकि वे अभी भी OOP को अनिवार्य प्रक्रियात्मक प्रोग्रामिंग के साथ मिला रहे हैं।


5

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

डेटा बाइंडिंग के साथ ऐसा करने में चार समस्याएं हैं :

  1. जावा कंस्ट्रक्टर्स मेटा-डेटा को प्रतिबिंब नामों को बनाए नहीं रखते हैं।
  2. जावा कन्स्ट्रक्टर्स (और विधियाँ) में नामांकित पैरामीटर (जिन्हें लेबल भी कहा जाता है) नहीं होते हैं, इस प्रकार यह कई मापदंडों के साथ भ्रमित हो जाता है।
  3. जब किसी अन्य अपरिवर्तनीय वस्तु को विरासत में दिया जाता है, तो कंस्ट्रक्टरों का उचित क्रम कहा जाना चाहिए। यह बल्कि केवल एक क्षेत्र को छोड़ देने और गैर-फाइनल में से एक को छोड़ने के लिए मुश्किल हो सकता है।
  4. अधिकांश बाइंडिंग प्रौद्योगिकियां (जैसे स्प्रिंग एमवीसी डेटा बाइंडिंग, हाइबरनेट, आदि ...) केवल नो-आर्ग डिफॉल्ट कंस्ट्रक्टर्स के साथ काम करेगी (ऐसा इसलिए था क्योंकि एनोटेशन हमेशा मौजूद नहीं थे)।

आप @ConstructorPropertiesअपरिवर्तनीय वस्तु बनाने के लिए एनोटेशन जैसे किसी अन्य उत्परिवर्ती बिल्डर ऑब्जेक्ट (आमतौर पर धाराप्रवाह) का उपयोग करके # 1 और # 2 को कम कर सकते हैं ।


5

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

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

यह उतना बड़ा नहीं है जितना कि यह होना चाहिए क्योंकि इसकी सख्त, हर भाषा पर लागू नहीं होती है और अधिकांश लोगों को अनिवार्य कोडिंग सिखाई गई थी।

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

यह भी याद रखें, कि अधिकांश प्रोग्रामर की स्थिति खराब है। अधिकांश प्रोग्रामिंग जो जंगली में की जाती है, वह भयानक है और इसकी वजह समझ और राजनीति की कमी है।


4

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


4

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

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


1

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

सामान्य तौर पर, आपके पास तीन प्रकार की कक्षाएं हैं: सामान्य, गैर-थ्रेड-सुरक्षित कक्षाएं, जिन्हें सावधानीपूर्वक संरक्षित और संरक्षित किया जाना है; अपरिवर्तनीय कक्षाएं, जिनका उपयोग स्वतंत्र रूप से किया जा सकता है; और उत्परिवर्तनीय, थ्रेड-सुरक्षित कक्षाएं जिनका उपयोग स्वतंत्र रूप से किया जा सकता है लेकिन जिन्हें अत्यधिक सावधानी से लिखा जाना चाहिए। पहला प्रकार परेशान करने वाला है, सबसे बुरा उन लोगों के साथ है जिन्हें तीसरे प्रकार का माना जाता है। बेशक, पहला प्रकार लिखने के लिए आसान हैं।

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

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

पीएस के रूप में, मुझे लगता है कि जावा गेटर्स और सेटर हाथ से निकल रहे हैं। चेक इस बाहर।


7
अपरिवर्तनीय स्थिति अभी भी राज्य है।
जेरेमी हीलर

3
@ जेरेमीहाइलर: सच है, लेकिन यह ऐसी चीज की स्थिति है जो पारस्परिक है । यदि कुछ भी नहीं बदलता है, तो केवल एक राज्य है, जो एक ही चीज है, जिसमें कोई भी राज्य नहीं है।
राल्फचिनपिन

1

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

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

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

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

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


1

म्यूटेबल ऑब्जेक्ट्स का उपयोग तब किया जाता है जब आपको ऑब्जेक्ट को इंस्टेंट करने के बाद कई गुना मान सेट करना होता है।

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

इसका एक उदाहरण रिपोर्ट ऑब्जेक्ट है, जिसमें फ़ॉन्ट, ओरिएंटेशन आदि के लिए सेटर्स हैं।

शॉर्ट के लिए: म्यूटेबल तब उपयोगी होते हैं जब आपके पास किसी ऑब्जेक्ट पर सेट करने के लिए बहुत सारी स्थिति होती है और यह बहुत लंबे समय तक निर्माण हस्ताक्षर करने के लिए व्यावहारिक नहीं होगा।

EDIT: बिल्डर पैटर्न का उपयोग ऑब्जेक्ट के पूरे राज्य को बनाने के लिए किया जा सकता है।


3
यह इस तरह प्रस्तुत किया जाता है जैसे तात्कालिकता के बाद परिवर्तनशीलता और परिवर्तन कई मूल्यों को निर्धारित करने का एकमात्र तरीका है। पूर्णता के लिए ध्यान दें कि बिल्डर पैटर्न समान या अधिक शक्तिशाली क्षमताओं को प्रदान करता है और अपरिवर्तनीयता का त्याग करने के लिए किसी की आवश्यकता नहीं होती है। new ReportBuilder().font("Arial").orientation("landscape").build()
gnat

1
"बहुत लंबे निर्माणकर्ता के हस्ताक्षर होना व्यावहारिक नहीं होगा।": आप हमेशा मापदंडों को छोटी वस्तुओं में वर्गीकृत कर सकते हैं और इन वस्तुओं को निर्माणकर्ता के मापदंडों के रूप में पास कर सकते हैं।
जियोर्जियो

1
यदि आप 10 या 15 विशेषताओं को सेट करना चाहते हैं तो क्या करें? इसके अलावा यह अच्छा नहीं लगता है कि नाम का एक तरीका withFontए रिटर्न देता है Report
ट्यूलेंस कोरडोवा

1
जहाँ तक 10 या 15 विशेषताओं को स्थापित करने के लिए, ऐसा करने का कोड अजीब नहीं होगा यदि (1) जावा जानता था कि उन सभी मध्यवर्ती वस्तुओं के निर्माण को कैसे समाप्त किया जाए, और यदि (2) फिर से, नाम मानकीकृत किए गए। यह अपरिवर्तनीयता की समस्या नहीं है; यह जावा के साथ एक समस्या है कि यह अच्छी तरह से कैसे करना है, यह नहीं जानता।
cHao

1
@ चाओ 20 विशेषताओं के लिए एक कॉल चेन बनाना बदसूरत है। बदसूरत कोड खराब गुणवत्ता कोड होता है।
ट्यूलेंस कोरडोवा

1

मुझे लगता है कि उत्परिवर्तित वस्तुओं का उपयोग करना अनिवार्य सोच से उपजा है: आप परिणामी परिवर्तनशील चरों की सामग्री को चरण दर चरण बदलकर (संगणना प्रभाव द्वारा गणना) करते हैं।

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

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

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

तो, क्यों कई प्रोग्रामर उत्परिवर्ती वस्तुओं का उपयोग करते हैं? दो कारणों से IMHO:

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

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

1
मैं एक FP विशेषज्ञ नहीं हूं लेकिन AFAIK (1) धागा संचार एक धागे में एक अपरिवर्तनीय मूल्य बनाकर और इसे दूसरे में पढ़ने के द्वारा प्राप्त किया जा सकता है (फिर, AFAIK, यह एर्लांग दृष्टिकोण है) (2) AFAIK एक संपत्ति सेट करने के लिए संकेतन बहुत अधिक नहीं बदलता है (उदाहरण के लिए हास्केल सेटप्रॉपर्टी रिकॉर्ड वैल्यू जावा रिकॉर्ड.सेटप्रोपरेटी (वैल्यू)) से मेल खाती है, केवल शब्दार्थ बदल जाता है क्योंकि संपत्ति सेट करने का परिणाम एक नया अपरिवर्तनीय रिकॉर्ड है।
जियोर्जियो

1
"दोनों के पास एक साझा ऑब्जेक्ट का संदर्भ होना चाहिए जो एक डेटा डाल सकता है और दूसरा इसे पढ़ सकता है": अधिक सटीक रूप से, आप किसी ऑब्जेक्ट को अपरिवर्तनीय बना सकते हैं (सभी सदस्य C ++ या जावा में अंतिम) और सभी सामग्री सेट करें निर्माता। फिर इस अपरिवर्तनीय वस्तु को उत्पादक धागे से उपभोक्ता धागा को सौंप दिया जाता है।
गियोर्जियो

1
(२) मैंने पहले ही प्रदर्शन को अपरिवर्तनीय स्थिति का उपयोग न करने के कारण सूचीबद्ध किया है। (1) के संबंध में, बेशक, जो तंत्र थ्रेड के बीच संचार को लागू करता है, उसे कुछ परिवर्तनशील स्थिति की आवश्यकता होती है, लेकिन आप इसे अपने प्रोग्रामिंग मॉडल से छिपा सकते हैं, उदाहरण के लिए अभिनेता मॉडल ( en.wikipedia.org/wiki/Actor_model ) देखें। इसलिए भले ही निचले स्तर पर आपको संचार को कार्यान्वित करने के लिए पारस्परिकता की आवश्यकता हो, आपके पास एक ऊपरी अमूर्त स्तर हो सकता है जिसमें आप थ्रेड के बीच अपरिवर्तनीय वस्तुओं को आगे और पीछे भेजते हैं।
गियोर्जियो

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

1

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

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

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


1

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

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

case class Person(id: String, firstName: String, lastName: String)

val joe = Person("123", "Joe", "Doe")
val spouse = Person(id = "124", firstName = "Mary", lastName = "Moe")
val joeMarried = joe.copy(lastName = "Doe-Moe")

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


इससे किए गए कुछ बिंदुओं पर पर्याप्त मात्रा में कुछ भी नहीं लगता है और पूर्व 24 उत्तरों में समझाया गया है
gnat

@gnat पिछले उत्तरों में से कौन सी बात यह बताती है कि अधिकांश मुख्यधारा की भाषाएँ शालीनता का समर्थन नहीं करती हैं? मुझे लगता है कि बिंदु केवल नहीं बनाया गया है (मैंने जाँच की), लेकिन यह IMHO एक महत्वपूर्ण बाधा है।
हंस-पीटर स्टॉर

यह एक उदाहरण के लिए जावा में इस मुद्दे को समझा गहराई में चला जाता है। और कम से कम 3 अन्य उत्तर अप्रत्यक्ष रूप से इससे संबंधित हैं
gnat

0

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

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

public abstract class FinalBase {

    private final int variable; // Unset

    /* if final really means immutable than
     * I shouldn't be able to set the variable
     * but I can.
     */
    public FinalBase(int variable) { 
        this.variable = variable;
    }

    public int getVariable() {
        return variable;
    }

    public abstract void method();
}

// This is not fully necessary for this example
// but helps you see how to set the final value 
// in a sub class.
public class FinalSubclass extends FinalBase {

    public FinalSubclass(int variable) {
        super(variable);
    }

    @Override
    public void method() {
        System.out.println( getVariable() );
    }

    @Override
    public int getVariable() {

        return super.getVariable();
    }

    public static void main(String[] args) {
        FinalSubclass subclass = new FinalSubclass(10);
        subclass.method();
    }
}

-1

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


1
मुझे अभी तक यह नहीं मिला कि मैंने OOP की उत्पत्ति के बारे में कहां पढ़ा है, लेकिन विकिपीडिया के अनुसार , एक बड़े कंटेनर शिपिंग कंपनी OOCL की कुछ एकीकृत क्षेत्रीय सूचना प्रणाली स्मालटाक में लिखी गई है।
एलेक्सी

-2

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


5
finalवास्तव में अपरिवर्तनीयता की ओर एक कदम के बारे में 1/4 है । यह देखकर नहीं कि आप इसका उल्लेख क्यों करते हैं।
cHao

क्या आपने वास्तव में मेरी पोस्ट पढ़ी है?
राल्फ एच

क्या आपने वास्तव में प्रश्न पढ़ा है? :) यह पूरी तरह से कुछ नहीं करना था final। इस संदर्भ में इसे लाने से वास्तव में final"उत्परिवर्तित" के साथ एक अजीब सा भ्रम हो जाता है, जब कुछ प्रकार के उत्परिवर्तन finalको रोकना बहुत महत्वपूर्ण है। (बीटीडब्लू, मेरे
डाउनवोट

मैं यह नहीं कह रहा हूं कि आपके पास यहां कहीं भी एक वैध बिंदु नहीं है। मैं कह रहा हूं कि आप इसे बहुत अच्छी तरह से नहीं समझा रहे हैं। मुझे लगता है कि आप कहाँ इसके साथ जा रहे हैं, लेकिन आप आगे जाने की जरूरत है देखते हैं। जैसा है, यह सिर्फ भ्रमित दिखता है।
19

1
वास्तव में बहुत कम मामले हैं जहां एक उत्परिवर्तित वस्तु की आवश्यकता होती है । ऐसे मामले हैं जहां कोड स्पष्ट होगा, या कुछ मामलों में तेजी से, उत्परिवर्तित वस्तुओं का उपयोग करके। लेकिन विचार करें कि डिजाइन पैटर्न के विशाल बहुमत मूल रूप से भाषाओं के लिए कार्यात्मक प्रोग्रामिंग का सिर्फ आधा-मिश्रित प्रतिपादन हैं जो मूल रूप से इसका समर्थन नहीं करते हैं। इसका मजेदार हिस्सा यह है कि, FP को केवल बहुत ही चुनिंदा स्थानों (जहाँ दुष्प्रभाव हो सकते हैं) में परिवर्तनशीलता की आवश्यकता होती है, और वे स्थान आम तौर पर संख्या, आकार और दायरे में बहुत सीमित होते हैं।
15:13 बजे cHao
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.