एक वस्तु को एक विधि में बदलना जो वस्तु को बदलता है, क्या यह एक सामान्य (विरोधी) पैटर्न है?


17

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

पैटर्न वह है जहां एक वस्तु को एक या अधिक विधियों के तर्क के रूप में पारित किया जाता है, जिसमें से सभी वस्तु की स्थिति को बदलते हैं, लेकिन इनमें से कोई भी वस्तु वापस नहीं करता है। तो यह पास पर निर्भर है (इस मामले में) C # /। NET के संदर्भ प्रकृति द्वारा।

var something = new Thing();
// ...
Foo(something);
int result = Bar(something, 42);
Baz(something);

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

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

var something1 =  new Thing();
// ...

// Let's return a new instance of Thing
var something2 = Foo(something1);

// Let's use out param to 'return' other info about the operation
int result;
var something3 = Bar(something2, out result);

// If necessary, let's capture and make explicit complex changes
var changes = Baz(something3)
something3.Apply(changes);

मेरे लिए ऐसा लगता है कि पहला पैटर्न मान्यताओं पर चुना गया है

  • यह कम काम है, या कोड की कम लाइनों की आवश्यकता है
  • यह हमें ऑब्जेक्ट को बदलने और कुछ अन्य जानकारी को वापस करने की अनुमति देता है
  • यह अधिक कुशल है क्योंकि हमारे पास कम उदाहरण हैं Thing

मैं एक विकल्प का वर्णन करता हूं, लेकिन इसे प्रस्तावित करने के लिए, मूल समाधान के खिलाफ तर्क देने की आवश्यकता है। क्या, यदि कोई हो, तो इस मामले को सुलझाने के लिए तर्क दिया जा सकता है कि मूल समाधान एक विरोधी पैटर्न है?

और क्या, अगर कुछ भी, मेरे वैकल्पिक समाधान के साथ गलत है?



1
@DaveHillier धन्यवाद, मैं इस शब्द से परिचित था, लेकिन उसने संबंध नहीं बनाया था।
मिकिएल वैन ओस्टरहौट

जवाबों:


9

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

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

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


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

@ टेटस्टाइन मुझे पता है कि यह पुराना उत्तर है, लेकिन मुझे आपके सुझाव को ठोस शब्दों में अंतिम पैराग्राफ में देखने में परेशानी हो रही है। क्या आप विस्तृत या एक उदाहरण दे सकते हैं?
एरोनल्स

@AaronLS - Bar(something)(और स्थिति को संशोधित करने के बजाय something), के प्रकार Barका सदस्य बनाते हैं somethingsomething.Bar(42)उत्परिवर्तन के लिए और अधिक होने की संभावना है something, जबकि यह भी आप OO उपकरण (निजी राज्य, इंटरफेस, आदि) का उपयोग करने के रक्षा करने के लिए अनुमति somethingकी राज्य
Telastyn

14

जब विधियों को उचित रूप से नामित नहीं किया जाता है

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

void AddMessageToLog(Logger logger, string msg)
{
    //...
}

या

void StripInvalidCharsFromName(Person p)
{
// ...
}

या

void AddValueToRepo(Repository repo,int val)
{
// ...
}

या

void TransferMoneyBetweenAccounts(Account source, Account destination, decimal amount)
{
// ...
}

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

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


आप सही हैं, नाम बदलने की विधि को सुधारने से साइड-इफेक्ट स्पष्ट होने से स्थिति में सुधार हो सकता है। यह कठिन हो सकता है, अगर संशोधन ऐसे हों कि संक्षिप्त विधि नाम संभव न हो।
मिकिएल वैन ओस्टरहौट

2
@ मिचिअलवू: यदि कॉन्सियस मेथड नेमिंग संभव नहीं लगता है, तो आपकी विधि गलत चीजों को एक साथ काम करने के लिए एक कार्यात्मक अमूर्त निर्माण के बजाय समूहित करती है (और यह दुष्प्रभाव के साथ या बिना सच है)।
डॉक ब्राउन

4

हां, http://codebetter.com/matthewpodwysocki/2008/04/30/side-effecting-functions-are-code-smells/ देखें, जिसमें से एक उदाहरण के लिए लोगों का कहना है कि अप्रत्याशित दुष्प्रभाव बुरे हैं।

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


मैं ओपी को "अपेक्षित दुष्प्रभावों" के रूप में वर्णित कर रहा हूं। उदाहरण के लिए, एक प्रतिनिधि आप किसी प्रकार के एक इंजन को पास कर सकते हैं जो सूची में प्रत्येक तत्व पर संचालित होता है। वह मूल रूप से क्या ForEach<T>करता है।
रॉबर्ट हार्वे

@RobertHarvey तरीकों के बारे में शिकायतों को ठीक से नाम नहीं दिया जा रहा है, और साइड इफेक्ट्स का पता लगाने के लिए कोड को पढ़ने के बारे में, उन्हें निश्चित रूप से साइड-इफेक्ट की उम्मीद नहीं है।
btilly

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

@ रोबर्टहवे मैं सहमत हूँ। कुंजी यह है कि महत्वपूर्ण दुष्प्रभावों के बारे में जानना बहुत महत्वपूर्ण है, और ध्यान से प्रलेखित करने की आवश्यकता है (अधिमानतः विधि के नाम पर)।
btilly

मैं कहूंगा कि यह अप्रत्याशित और गैर-स्पष्ट दुष्प्रभावों का मिश्रण है। लिंक के लिए धन्यवाद।
मिचेल वैन ओस्टरहौट

3

सबसे पहले, यह "पास संदर्भ प्रकृति द्वारा पास" पर निर्भर नहीं करता है, यह वस्तुओं पर परस्पर संदर्भ प्रकार होने पर निर्भर करता है। गैर-कार्यात्मक भाषाओं में लगभग हमेशा ऐसा ही होता है।

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

मैं यह नहीं कहूंगा कि यह एक विरोधी पैटर्न के स्तर तक बढ़ जाता है, लेकिन कुछ जागरूक होने के लिए।


2

मेरा तर्क है कि A.Do(Something)संशोधन somethingऔर something.Do()संशोधन के बीच बहुत कम अंतर है something। में या तो मामले में, यह लागू विधि के नाम से ही स्पष्ट होना चाहिए somethingसंशोधित किया जाएगा। यदि यह विधि के नाम से स्पष्ट नहीं है, चाहे वह somethingपैरामीटर this, या पर्यावरण का हिस्सा हो, तो इसे संशोधित नहीं किया जाना चाहिए


1

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

var users = Dependency.Resolve<IGetUsersQuery>().GetAll();

var excludeAdminUsersFilter = new ExcludeAdminUsersFilter();
var filterByAnotherCriteria = new AnotherCriteriaFilter();

excludeAdminUsersFilter.Apply(users);
filterByAnotherCriteria.Apply(users); 

और हाँ, आप फ़िल्टरिंग को किसी अन्य विधि में स्थानांतरित करके इसे बहुत सुंदर बना सकते हैं, इसलिए आप कुछ की तर्ज पर इसे समाप्त करेंगे:

var users = Dependency.Resolve<IGetUsersQuery>().GetAll();
Filter(users);

Filter(users)फ़िल्टर से ऊपर कहां निष्पादित होगा।

मुझे याद नहीं है कि वास्तव में मैं इससे पहले कहाँ आया था, लेकिन मुझे लगता है कि इसे पाइपलाइन को फ़िल्टर करने के रूप में संदर्भित किया गया था।


0

मुझे यकीन नहीं है कि प्रस्तावित नया समाधान (वस्तुओं की नकल) एक पैटर्न है। समस्या, जैसा कि आपने बताया है, कार्यों का बुरा नामकरण है।

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

आपके समाधान के बारे में, कुछ अवलोकन:

  • अनुप्रयोगों को विभिन्न पहलुओं से डिज़ाइन किया जाता है: जब किसी ऑब्जेक्ट का उपयोग केवल मान रखने के लिए किया जाता है, या घटक सीमाओं के पार किया जाता है, तो वस्तु के अंतर को बाहरी रूप से बदलना बुद्धिमानी है बजाय इसे बदलने के कि कैसे विवरण के साथ।
  • क्लोनिंग ऑब्जेक्ट्स स्मृति आवश्यकताओं को फुलाते हैं, और कई मामलों में असंगत राज्यों में समतुल्य वस्तुओं के अस्तित्व की ओर जाता है ( बाद में Xबन Yगया f(), लेकिन Xवास्तव Yमें है) और संभवतः अस्थायी असंगतता।

आप जिस समस्या का समाधान करने की कोशिश कर रहे हैं, वह एक वैध है; हालाँकि, भारी भरकम अतिरेक के साथ, समस्या को दरकिनार किया जाता है, हल नहीं किया जाता है।


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

1
@RobertHarvey +1, अच्छा अवलोकन, मैं सहमत हूँ, इसे संपादित करेंगे।
सीएमआर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.