java - गेटर्स और सेटर्स खराब डिजाइन हैं? विरोधाभासी सलाह देखा




oop setter (11)

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

इस प्रश्न का उत्तर यहां दिया गया है:

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

मेरे कोड पर एक त्वरित नज़र डालने के बाद, अधिकांश लोग गेम के तर्क के लिए वास्तव में आवश्यक बाकी की तुलना में गेटर्स और सेटर्स (60%) थे।

कुछ Google खोजों ने दावा किया है कि गेटर्स और सेटर बुरा हैं, जबकि अन्य ने दावा किया है कि वे अच्छे ओओ अभ्यास और महान कार्यक्रमों के लिए आवश्यक हैं।

तो मुझे क्या करना चाहिए? यह कौन सा होना चाहिए? क्या मुझे अपने निजी चर के लिए मेरे गेटर्स और सेटर्स बदलना चाहिए, या क्या मुझे उनके साथ रहना चाहिए?


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

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


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

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


गेटर्स और सेटर्स ऑब्जेक्ट उन्मुख प्रोग्रामिंग में encapsulation की अवधारणा को लागू करते हैं।

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

गेटर्स और सेटर्स रखने के कुछ फायदे हैं:

1. संशोधित वर्ग का उपयोग करने वाले कोड में संशोधन किए बिना भविष्य में परिवर्तन की अनुमति देना।

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

उदाहरण के लिए, value setValue कि एक सेट setValue विधि है जो किसी ऑब्जेक्ट में value निजी चर सेट करती है:

public void setValue(int value)
{
    this.value = value;
}

लेकिन फिर, एक नई आवश्यकता थी जिसे ट्रैक की संख्या बदलने के लिए आवश्यक था। जगह में सेटटर के साथ, परिवर्तन काफी मामूली है:

public void setValue(int value)
{
    this.value = value;
    count++;
}

यदि value फ़ील्ड सार्वजनिक था, तो बाद में वापस आने का कोई आसान तरीका नहीं है और एक काउंटर जोड़ें जो मूल्य को बदलने के समय की संख्या को ट्रैक करता है। इसलिए, गेटर्स और सेटर्स होने के बाद परिवर्तन के लिए वर्ग "भावी-प्रमाण" वर्ग का एक तरीका है जो बाद में आ सकता है।

2. उन साधनों को लागू करना जिनके द्वारा वस्तु का उपयोग किया जा सकता है।

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

उदाहरण के लिए, एक ImmutableArray ऑब्जेक्ट में myArray नामक एक int सरणी होती है। यदि सरणी सार्वजनिक क्षेत्र थी, तो यह अपरिवर्तनीय नहीं होगी:

ImmutableArray a = new ImmutableArray();
int[] b = a.myArray;
b[0] = 10;      // Oops, the ImmutableArray a's contents have been changed.

वास्तव में अपरिवर्तनीय सरणी को लागू करने के लिए, सरणी ( getArray विधि) के लिए एक गेटर लिखा जाना चाहिए ताकि यह इसकी सरणी की एक प्रति लौटाए:

public int[] getArray()
{
    return myArray.clone();
}

और यहां तक ​​कि यदि निम्न होता है:

ImmutableArray a = new ImmutableArray();
int[] b = a.getArray();
b[0] = 10;      // No problem, only the copy of the array is affected.

ImmutableArray वास्तव में अपरिवर्तनीय है। किसी ऑब्जेक्ट के वेरिएबल्स का एक्सपोज़ करने से यह उन तरीकों से छेड़छाड़ की अनुमति मिल जाएगी जो इरादे से नहीं हैं, बल्कि केवल कुछ तरीकों (गेटर्स और सेटर्स) को उजागर करने के लिए, ऑब्जेक्ट को इच्छित तरीकों से छेड़छाड़ की जा सकती है।

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

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


मुझे सच में नहीं लगता कि वे बुरे हैं। लेकिन मुझे ऐसी दुनिया में रहना अच्छा लगेगा जहां मुझे कभी भी इसका इस्तेमाल नहीं करना पड़ेगा जब तक कि मुझे वास्तव में आवश्यकता नहीं होती।

एक उदाहरण जो मैंने ऊपर पढ़ा था वह future-proofing आपके कोड को future-proofing रहा था। उदाहरण के लिए:

public void setValue(int value)
{
    this.value = value;
}

फिर, आवश्यकताएं बदलती हैं और आपको यह ट्रैक करने की आवश्यकता होती है कि मूल्य कितनी बार सेट किया गया था।

इसलिए:

public void setValue(int value)
{
    this.value = value;
    count++;
}

ये सुन्दर है। मैं समझ गया। हालांकि, रुबी में, क्या निम्नलिखित एक ही उद्देश्य की सेवा नहीं करेंगे?

someobject.my_value = 100

बाद में, आपको my_value सेट की गई संख्या को ट्रैक करने की आवश्यकता है। वैसे तो, क्या आप केवल उस समय सेटर को ओवरराइड नहीं कर सकते थे और केवल तब ?

def my_value=(value)
    @my_value = value
    @count++
end

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

जब मैं सी # पूर्ण समय में विकास कर रहा था, हमने हर समय सार्वजनिक संपत्तियों का उपयोग किया और आवश्यकता होने पर केवल कस्टम गेटर्स / सेटर्स किया। एक आकर्षण की तरह काम किया और यह कुछ तोड़ नहीं था।


मेरी राय यह है कि अच्छे कार्यक्रमों के लिए गेटर्स और सेटर्स एक आवश्यकता है। उनके साथ चिपके रहें, लेकिन अनावश्यक गेटर्स / सेटर्स न लिखें - सभी चरों से सीधे निपटने के लिए हमेशा आवश्यक नहीं है।


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

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


यह एक फिसलन ढलान है।

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

ऐसे वर्ग का भी मामला है जो कुछ "नियंत्रण knobs" का खुलासा करता है; आपकी कार रेडियो के यूआई को शायद getVolume , setVolume , getChannel , और setChannel जैसे कुछ को उजागर करने के रूप में समझा जा सकता है, लेकिन इसकी असली कार्यक्षमता सिग्नल प्राप्त कर रही है और ध्वनि उत्सर्जित कर रही है। लेकिन उन knobs अधिक कार्यान्वयन विस्तार का पर्दाफाश नहीं करते हैं; आप उन इंटरफेस सुविधाओं से नहीं जानते हैं कि रेडियो ट्रांजिस्टर, ज्यादातर सॉफ्टवेयर, या वैक्यूम ट्यूब हैं।

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

कितना दुष्ट"? ज़रुरी नहीं। लेकिन हर बार जब आप एक मूल्य डालते हैं और दोनों का पर्दाफाश करते get ... और उस मूल्य पर ... set , खुद से पूछें क्यों, और उस वस्तु का प्रतिमान वास्तव में क्या है। यदि एकमात्र उत्तर आप स्वयं को दे सकते हैं, "मेरे लिए यह मूल्य रखने के लिए", तो शायद ओओ के अलावा कुछ भी यहां जा रहा है।


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

// Game
private int score;
public void setScore(int score) { this.score = score; }
public int getScore() { return score; }
// Usage
game.setScore(game.getScore() + ENEMY_DESTROYED_SCORE);

यह होना चाहिए

// Game
private int score;
public int getScore() { return score; }
public void addScore(int delta) { score += delta; }
// Usage
game.addScore(ENEMY_DESTROYED_SCORE);

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

विचार उन तरीकों को बनाना है जो सीधे काम करना चाहते हैं। एक उदाहरण दुश्मनों की "जिंदा" स्थिति को सेट करने का तरीका होगा। आप एक सेटएलीव (बूलियन जीवित) विधि रखने के लिए लुभाने वाले हो सकते हैं। इसके बजाय आपके पास होना चाहिए:

private boolean alive = true;
public boolean isAlive() { return alive; }
public void kill() { alive = false; }

इसका कारण यह है कि यदि आप कार्यान्वयन को बदलते हैं कि चीजों में अब "जीवित" बुलियन नहीं बल्कि एक "हिट पॉइंट" मान है, तो आप इसे पहले लिखने वाले दो तरीकों के अनुबंध को तोड़ने के बिना इसे बदल सकते हैं:

private int hp; // Set in constructor.
public boolean isAlive() { return hp > 0; } // Same method signature.
public void kill() { hp = 0; } // Same method signature.
public void damage(int damage) { hp -= damage; }

वे बिल्कुल बुराई हैं।

दुर्भाग्य से @coobird वे पूरी तरह से "encapsulation की अवधारणा को लागू नहीं करते हैं", वे सब कुछ आपको लगता है कि आप डेटा को encapsulating कर रहे हैं जब वास्तव में आप एक संपत्ति के माध्यम से विधि भव्यता के भ्रम के साथ डेटा उजागर कर रहे हैं। कोई भी गेटर / सेटर एक सार्वजनिक क्षेत्र बेहतर करता है।

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

object.field = value;

अधिक संज्ञानात्मक तीव्र के बजाय

object.setField(value);

जहां क्लाइंट को अब गेटटर / सेटर विधि की जांच करनी चाहिए ताकि यह देखने के लिए कि इसका कोई दुष्प्रभाव है या नहीं।

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

public void kill(){
    isAlive = false;
    removeFromWorld(this);
}

के बजाय

public void setAlive(boolean isAlive){
    this.isAlive = isAlive;
    if (isAlive)
        addToWorld(this);
    else
        removeFromWorld(this);
}

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

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

यहां मेरा रान समाप्त होता है, इसके बारे में खेद है;)


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

यह वास्तव में स्थिति पर निर्भर करता है - कभी-कभी आप वास्तव में सिर्फ एक गूंगा डेटा ऑब्जेक्ट चाहते हैं।





getter