2009-05-29 9 views
8

क्या लोग व्यावहारिक रूप से रक्षात्मक गेटर्स/सेटर्स का उपयोग करते हैं? मेरे लिए, 99% बार जब आप किसी ऑब्जेक्ट में उसी ऑब्जेक्ट संदर्भ की प्रतिलिपि बनाने के लिए सेट करते हैं, और आप उस ऑब्जेक्ट में किए गए परिवर्तनों का इरादा रखते हैं जो इसे सेट किया गया था। आप setDate (Date dt) और बाद में डीटी संशोधित करते हैं, कौन परवाह करता है? जब तक कि मैं कुछ बुनियादी अपरिवर्तनीय डेटा बीन नहीं चाहता हूं, जिसमें केवल प्राइमेटिव हैं और शायद डेट की तरह कुछ सरल हो, मैं इसका कभी भी उपयोग नहीं करता।क्या क्लोन() वास्तव में कभी भी उपयोग किया जाता है? गेटर्स/सेटर्स में रक्षात्मक प्रतिलिपि के बारे में क्या?

क्लोन तक, इस बात के मुद्दे हैं कि कॉपी कितनी गहरी या उथली है, इसलिए यह पता लगाना कि "ऑब्जेक्ट क्लोन करते समय क्या बाहर निकलना है। मुझे लगता है कि मैंने केवल एक या दो बार clone() का उपयोग किया है, और यह ऑब्जेक्ट की वर्तमान स्थिति की प्रतिलिपि बनाना था क्योंकि एक और थ्रेड (यानी सत्र में एक ही ऑब्जेक्ट तक पहुंचने वाला एक और HTTP अनुरोध) इसे संशोधित कर सकता है। -

संपादित करें एक टिप्पणी मैं नीचे बना अधिक सवाल यह है:

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

+1

आप यहां दो प्रश्न पूछ रहे हैं। आपको उन्हें विभाजित करना चाहिए। – Welbog

+0

वे "हाथ में हाथ" के रूप में जाना था। – GreenieMeanie

उत्तर

3

इन दोनों मुद्दों के लिए, बिंदु राज्य का स्पष्ट नियंत्रण है। ऐसा हो सकता है कि ज्यादातर समय आप इन चीजों के बारे में सोचने के बिना "दूर हो जाएं"। यह कम सत्य होता है क्योंकि आपका आवेदन बड़ा हो जाता है और यह राज्य के बारे में तर्क और वस्तुओं के बीच प्रचार करने के लिए कठिन हो जाता है।

आप पहले से ही एक प्रमुख कारण का उल्लेख कर चुके हैं कि आपको इस पर नियंत्रण क्यों रखना होगा - डेटा का सुरक्षित रूप से उपयोग करने में सक्षम होने पर एक और धागा इसे एक्सेस कर रहा था। यह भी इस तरह गलतियाँ करने के लिए आसान है:

class A { 
    Map myMap; 
} 


class B { 
    Map myMap; 
    public B(A a) 
    { 
     myMap = A.getMap();//returns ref to A's myMap 
    } 
    public void process(){ // call this and you inadvertently destroy a 
      ... do somethign destructive to the b.myMap... 
    } 
} 

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

+0

आमतौर पर एपीआई दस्तावेज़ों में गेटर्स/सेटर्स को "इसे प्राप्त या सेट करने" से अधिक दस्तावेज नहीं किया जाता है, इसलिए आपको नहीं पता कि यह वास्तव में क्या कर रहा है ... – GreenieMeanie

+0

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

+1

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

11
जोश बलोच के प्रभावी जावा से

:

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

Item 24: Make defensive copies when needed

+0

हां, यह एक अच्छा पढ़ा है। इसके अतिरिक्त - क्या आपकी वस्तु गहराई से अपरिवर्तनीय है? यदि ऐसा है, तो क्लोन() के बारे में चिंता न करें। यदि नहीं, तो आप उत्परिवर्तनीय सदस्यों को डुप्लिकेट करने के लिए क्लोन को ओवरराइड करना चाहेंगे। –

+0

मैंने उस पुस्तक को पढ़ा है - अच्छा पढ़ा। हालांकि, मैं और अधिक वास्तविक और व्यावहारिक उदाहरणों की तलाश में हूं। उदाहरण के लिए, यदि हमने जावा एपीआई के अंदर देखा है, तो क्या कक्षाओं में से कोई भी वास्तव में ऐसा कर रहा है? – GreenieMeanie

+0

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

1

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

5

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

Date now = new Date(); 
someObject.setDate(now); 
// another use of "now" that expects its value to not have changed 

तो:

  1. someObject संभवतः, "now" का मान बदल सकता है उपरोक्त विधि अर्थ जब बाद में उपयोग करता है चर सकता है इसकी तुलना में एक अलग मूल्य है, और
  2. "someObject से गुजरने के बाद यदि आप अपना मान बदलते हैं, और someObject ने रक्षात्मक प्रति नहीं बनाया है, तो आपनेकी आंतरिक स्थिति बदल दी है।

आपको या तो दोनों मामलों के खिलाफ संरक्षित किया जाना चाहिए, या आपको कोड के ग्राहक के आधार पर अनुमति या अस्वीकार करने की आपकी अपेक्षा को दस्तावेज करना चाहिए। एक और मामला तब होता है जब एक वर्ग में Map होता है और आप Map के लिए एक गेटर प्रदान करते हैं। यदि Map ऑब्जेक्ट की आंतरिक स्थिति का हिस्सा है और ऑब्जेक्ट पूरी तरह सेMap की सामग्री का प्रबंधन करता है, तो आपको कभीMap बाहर जाने दें। यदि आपको मानचित्र के लिए गेटटर प्रदान करना होगा, तो myMap के बजाय Collections.unmodifiableMap(myMap) वापस करें। यहां आप शायद संभावित लागत के कारण क्लोन या रक्षात्मक प्रतिलिपि बनाना नहीं चाहते हैं। अपने मानचित्र को लपेटकर वापस संशोधित किया जा सकता है ताकि इसे संशोधित नहीं किया जा सके, आप अपनी आंतरिक स्थिति को किसी अन्य वर्ग द्वारा संशोधित होने से बचा रहे हैं।

कई कारणों से, clone() अक्सर सही समाधान नहीं है। कुछ बेहतर समाधान कर रहे हैं:

  1. ही टिककर खेल के लिए:
    1. एक Map लौटने के बजाय, या तो keySet करने के लिए या Map.Entry करने के लिए या जो कुछ भी करने के लिए केवल Iterator रों लौट यह करने की जरूरत है कि क्या करना है ग्राहक कोड की अनुमति देता है । दूसरे शब्दों में, कुछ अपने आंतरिक स्थिति का केवल-पढ़ने के लिए दृश्य है कि अनिवार्य रूप से वापस लौटने या
    2. बल्कि एक Map लौटने से लौटें
    3. अपने परिवर्तनशील राज्य वस्तु Collections.unmodifiableMap()
    4. के समान अपरिवर्तनीय आवरण में लिपटे, एक get तरीका प्रदान कि एक कुंजी लेता है और मानचित्र से संबंधित मान देता है। यदि सभी ग्राहक Map के साथ करेंगे, तो इससे मूल्य प्राप्त हो जाएंगे, फिर ग्राहकों को Map स्वयं न दें; इसके बजाय, एक गेटर प्रदान करें जो Map की get() विधि को लपेटता है।
  2. कंस्ट्रक्टर्स के लिए:
    1. अपने वस्तु कंस्ट्रक्टर्स में उपयोग प्रतिलिपि कंस्ट्रक्टर्स कि में पारित कुछ भी की एक प्रतिलिपि बनाने के लिए परिवर्तनशील है।
    2. परिवर्तनीय मात्रा के बजाय, जब आप कर सकते हैं तो कन्स्ट्रक्टर तर्क के रूप में अपरिवर्तनीय मात्रा लेने के लिए डिज़ाइन करें। कभी-कभी new Date().getTime() द्वारा लंबे समय तक लौटने का अर्थ होता है, उदाहरण के लिए, Date ऑब्जेक्ट के बजाए।
    3. जितना संभव हो उतना राज्य final बनाएं, लेकिन याद रखें कि final ऑब्जेक्ट अभी भी म्यूटेबल हो सकता है और final सरणी अभी भी संशोधित की जा सकती है।

सभी मामलों में, अगर वहाँ जो परिवर्तनशील राज्य "मालिक" के बारे में एक सवाल है, टिककर खेल या setters या कंस्ट्रक्टर्स पर यह दस्तावेज़। इसे कहीं दस्तावेज करें।

यहाँ बुरा कोड का एक तुच्छ उदाहरण है:

import java.util.Date; 

public class Test { 
    public static void main(String[] args) { 
    Date now = new Date(); 
    Thread t1 = new Thread(new MyRunnable(now, 500)); 
    t1.start(); 
    try { Thread.sleep(250); } catch (InterruptedException e) { } 
    now.setTime(new Date().getTime()); // BAD! Mutating our Date! 
    Thread t2 = new Thread(new MyRunnable(now, 500)); 
    t2.start(); 
    } 

    static public class MyRunnable implements Runnable { 
    private final Date date; 
    private final int count; 

    public MyRunnable(final Date date, final int count) { 
     this.date = date; 
     this.count = count; 
    } 

    public void run() { 
     try { Thread.sleep(count); } catch (InterruptedException e) { } 
     long time = new Date().getTime() - date.getTime(); 
     System.out.println("Runtime = " + time); 
    } 
    } 
} 

आप यह देखना चाहिए कि 500 ​​msec के लिए प्रत्येक runnable सोता है, लेकिन इसके बजाय आप गलत समय जानकारी मिलती है। यदि आप रक्षात्मक प्रतिलिपि बनाने के लिए कन्स्ट्रक्टर बदलते हैं:

public MyRunnable(final Date date, final int count) { 
     this.date = new Date(date.getTime()); 
     this.count = count; 
    } 

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

नोट: सामान्य संग्रह को व्यवस्थित करते समय ConcurrentModificationException स्थिति को व्यवस्थित करने में विफलता का परिणाम है।

क्या आप रक्षात्मक रूप से कोड करना चाहिए? यदि आप की गारंटी दे सकते हैं कि विशेषज्ञ प्रोग्रामर की एक ही छोटी टीम हमेशा आपके प्रोजेक्ट को लिखने और बनाए रखने वाले व्यक्ति होगी, ताकि वे लगातार इस पर काम कर सकें ताकि वे परियोजना के ब्योरे की याददाश्त बनाए रख सकें, वही लोग काम करेंगे यह परियोजना के जीवनकाल के लिए, और यह परियोजना कभी "बड़ी" नहीं बन जाएगी, तो शायद आप ऐसा करने से दूर हो सकते हैं। लेकिन रक्षात्मक प्रोग्रामिंग की लागत सबसे दुर्लभ मामलों को छोड़कर बड़ी नहीं है - और लाभ बड़ा है। इसके अलावा: रक्षात्मक कोडिंग एक अच्छी आदत है। आप उन जगहों के आस-पास परिवर्तनीय डेटा को पारित करने की बुरी आदतों के विकास को प्रोत्साहित नहीं करना चाहते हैं, जिनके पास यह नहीं होना चाहिए। यह आपको कुछ दिन काट देगा। बेशक, यह सब आपके प्रोजेक्ट के आवश्यक अपटाइम पर निर्भर करता है।

+0

मेरा पूरा बिंदु (आपके उदाहरण के लिए) प्रश्न की भीख मांग रहा है - अगर आप बाद में "दिनांक अब" संशोधित करते हैं तो क्या वास्तव में यह महत्वपूर्ण है? दूसरे शब्दों में, क्या आपका प्रोग्राम क्रैश होगा या क्या यह बग पेश करेगा? – GreenieMeanie

+1

यह निश्चित रूप से बग पेश कर सकता है। – Eddie

+0

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

0

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

मेरे काम पर हम रक्षात्मक प्रोग्रामिंग के बारे में चिंता नहीं करते हैं, हालांकि यह एक बुरा खरगोश है। लेकिन ज्यादातर समय यह ठीक हो जाता है, लेकिन मुझे लगता है कि मैं इसे नज़दीकी रूप से देखने जा रहा हूं।

+0

क्या होगा यदि आप वस्तु के वर्ग प्रकार को प्रतिलिपि/क्लोन करने के लिए नहीं जानते हैं? अगर मैं आपको एक पशु देता हूं जिसे आप प्रतिलिपि बनाना चाहते हैं, तो आप किस वर्ग की कॉपी कन्स्ट्रक्टर का उपयोग करते हैं? पशु से विस्तार स्तनधारी, कुत्ता, बिल्ली, आदि है। उद्देश्य अगर क्लोन() यह है कि आप अपनी कक्षा के प्रकार को निर्धारित किए बिना एक प्रति प्राप्त कर सकते हैं। –

+0

जेडीके 1.5+ में आप क्लोन विधि का उपयोग कर सकते हैं और रिटर्न प्रकार को अपनी कक्षा में बदल सकते हैं (हालांकि लगभग कोई भी ऐसा नहीं करता क्योंकि उन्हें पुराने प्रतिबंधों में उपयोग किया जाता है)। अन्य कारणों से क्लोन टूट गया है। – Yishai

1

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

0

एक चीज जिसे मैं "रक्षात्मक प्रति चर्चा" में लापता हूं, प्रदर्शन पहलू है। यह आकलन आईएमएचओ प्रदर्शन बनाम पठनीयता/सुरक्षा/मजबूती का एक आदर्श उदाहरण है।

रक्षा प्रतियां रोपस्ट ओडी के लिए बहुत अच्छी हैं। लेकिन अगर आप इसे अपने ऐप के महत्वपूर्ण हिस्से में इस्तेमाल करते हैं तो यह एक प्रमुख प्रदर्शन मुद्दा हो सकता है। हमने हाल ही में यह चर्चा की थी जहां एक डेटा वेक्टर ने अपने डेटा को डबल [] मानों में संग्रहीत किया था। getValues ​​() value.clone() लौटा दिया। हमारे एल्गोरिदम में, getValues ​​() को के लिए विभिन्न ऑब्जेक्ट्स के बहुत के लिए बुलाया गया था।जब हम सोच रहे थे, कोड के इस साधारण टुकड़े को निष्पादित करने में इतना समय क्यों लगा, हमने कोड का निरीक्षण किया - रिटर्न वैल्यू के साथ रिटर्न वैल्यू। क्लोन() को बदल दिया और अचानक हमारे कुल निष्पादन का समय मूल के 1/10 से कम हो गया मूल्य। खैर - मुझे यह कहना जरूरी नहीं है कि हमने रक्षात्मकता को छोड़ना चुना है।

नोट: मैं सामान्य रूप से फिर से रक्षात्मक प्रतियां नहीं हूं। लेकिन क्लोन() आईएनजी जब आपके दिमाग का उपयोग करें!

0

मैं निम्नलिखित अभ्यास का उपयोग शुरू कर दिया है:

  1. अपनी कक्षाओं में प्रति कंस्ट्रक्टर्स बनाएं लेकिन उन्हें संरक्षित हैं। इसका कारण यह है कि नए ऑपरेटर का उपयोग करके वस्तुओं को बनाने से व्युत्पन्न वस्तुओं के साथ काम करते समय विभिन्न मुद्दों का कारण बन सकता है।

  2. एक प्रतिलिपि बनाने योग्य इंटरफ़ेस बनाएँ इस प्रकार है:

 
    public interface Copyable<T> { 
      public T copy(); 
    } 

प्रतिलिपि बनाने योग्य कॉल संरक्षित प्रतिलिपि निर्माता को लागू करने वर्गों के प्रति विधि है। व्युत्पन्न कक्षाएं तो super.Xxx (obj_to_copy) को कॉल कर सकती हैं; बेस क्लास कॉपी कन्स्ट्रक्टर का लाभ उठाने और आवश्यकतानुसार अतिरिक्त कार्यक्षमता जोड़ने के लिए।

तथ्य यह है कि जावा covariant return type का समर्थन करता है यह काम करता है। व्युत्पन्न कक्षाएं प्रतिलिपि() विधि को उचित रूप से कार्यान्वित करती हैं और उनके विशेष वर्ग के लिए एक प्रकार-सुरक्षित मान लौटाती हैं।

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

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