एसआरपी को लागू करने के व्यावहारिक तरीके क्या हैं?


11

यदि एक वर्ग एकल जिम्मेदारी सिद्धांत का उल्लंघन करता है, तो लोग क्या व्यावहारिक तकनीकों का उपयोग करते हैं?

मुझे पता है कि एक वर्ग को बदलने का केवल एक कारण होना चाहिए, लेकिन उस वाक्य को वास्तव में लागू करने के लिए व्यावहारिक तरीके की कमी है।

एकमात्र तरीका जो मैंने पाया वह वाक्य "द ......... चाहिए ......... ही है।" जहाँ पहला स्थान वर्ग नाम है और बाद में विधि (जिम्मेदारी) नाम है।

हालांकि, कभी-कभी यह पता लगाना मुश्किल है कि क्या वास्तव में एक जिम्मेदारी एसआरपी का उल्लंघन करती है।

क्या SRP की जाँच करने के और तरीके हैं?

ध्यान दें:

सवाल यह नहीं है कि एसआरपी का मतलब क्या है, बल्कि एक व्यावहारिक कार्यप्रणाली या एसआरपी को जांचने और लागू करने के लिए कई चरणों की श्रृंखला है।

अपडेट करें

रिपोर्ट वर्ग

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

उदाहरण यहाँ से है


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

@EmmadKareem इस नियम का उल्लेख हेड फ़र्स्ट ऑब्जेक्ट-ओरिएंटेड एनालिसिस एंड डिज़ाइन में किया गया था और ठीक यही मैंने इसके बारे में सोचा था। इसे लागू करने के लिए व्यावहारिक तरीके की कमी है। उन्होंने उल्लेख किया कि कभी-कभी जिम्मेदारियां डिजाइनर के लिए स्पष्ट नहीं होंगी और उन्हें यह समझने के लिए सामान्य ज्ञान का एक बड़ा उपयोग करना होगा कि क्या विधि वास्तव में इस वर्ग में होनी चाहिए या नहीं।
सांगो

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

और नीचे मतदाता कृपया बताएं कि पोस्ट में सुधार क्यों किया जाए ?!
सांगो

यह भी देखें:
gnat

जवाबों:


7

SRP कहता है, बिना किसी अनिश्चितता के, कि एक वर्ग को कभी भी बदलने का एक कारण होना चाहिए।

प्रश्न में "रिपोर्ट" वर्ग का पुनर्निर्माण, इसकी तीन विधियाँ हैं:

  • printReport
  • getReportData
  • formatReport

Reportहर विधि में उपयोग किए जा रहे अनावश्यक को अनदेखा करना , यह देखना आसान है कि यह SRP का उल्लंघन क्यों करता है:

  • "प्रिंट" शब्द का अर्थ किसी प्रकार के UI, या वास्तविक प्रिंटर से है। इस वर्ग में यूआई या प्रस्तुति तर्क की कुछ मात्रा शामिल है। यूआई आवश्यकताओं में बदलाव से Reportवर्ग में बदलाव की आवश्यकता होगी ।

  • "डेटा" शब्द का अर्थ किसी प्रकार की डेटा संरचना से है, लेकिन वास्तव में क्या निर्दिष्ट नहीं करता है (XML? JSON? CSV)। बावजूद, अगर रिपोर्ट की "सामग्री" कभी बदलती है, तो यह विधि होगी। एक डेटाबेस या एक डोमेन के लिए युग्मन है।

  • formatReportसामान्य रूप से एक विधि के लिए बस एक भयानक नाम है, लेकिन मैं इसे देखकर यह मानूंगा कि यह एक बार फिर यूआई के साथ कुछ करना है, और शायद यूआई का एक अलग पहलू है printReport। तो, एक और, असंबंधित कारण बदलने के लिए।

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

यहाँ समस्या का एक हिस्सा यह है कि आपने एक विशेष रूप से कांटेदार उदाहरण उठाया है। आपको शायद एक वर्ग नहीं बुलाया जाना चाहिए Report, भले ही वह केवल एक ही काम करता हो , क्योंकि ... क्या रिपोर्ट? अलग-अलग डेटा और अलग-अलग आवश्यकताओं के आधार पर सभी "रिपोर्ट" पूरी तरह से अलग जानवर नहीं हैं? और एक रिपोर्ट कुछ ऐसा नहीं है जो पहले से ही स्वरूपित है, या तो स्क्रीन के लिए या प्रिंट के लिए?

लेकिन, उस अतीत को देखते हुए, और एक काल्पनिक ठोस नाम - चलो इसे कहते हैं IncomeStatement(एक बहुत ही सामान्य रिपोर्ट) - एक उचित "SRPed" वास्तुकला में तीन प्रकार होंगे:

  • IncomeStatement- डोमेन और / या मॉडल वर्ग जिसमें शामिल हैं और / या स्वरूपित रिपोर्ट पर दिखाई देने वाली जानकारी की गणना करता है ।

  • IncomeStatementPrinter, जो शायद कुछ मानक इंटरफ़ेस को लागू करेगा IPrintable<T>। एक प्रमुख विधि है, Print(IncomeStatement)और शायद कुछ अन्य तरीके या गुण प्रिंट-विशिष्ट सेटिंग्स को कॉन्फ़िगर करने के लिए।

  • IncomeStatementRenderer, जो स्क्रीन रेंडरिंग को हैंडल करता है और प्रिंटर क्लास से काफी मिलता-जुलता है।

  • आप अंततः IncomeStatementExporter/ जैसे अधिक सुविधा-विशिष्ट वर्ग भी जोड़ सकते हैं IExportable<TReport, TFormat>

जेनेरिक और IoC कंटेनरों की शुरुआत के साथ आधुनिक भाषाओं में इसे काफी आसान बना दिया गया है। आपके अधिकांश एप्लिकेशन कोड को विशिष्ट IncomeStatementPrinterवर्ग पर निर्भर होने की आवश्यकता नहीं है , यह किसी भी प्रकार की प्रिंट करने योग्य रिपोर्ट का उपयोग कर सकता है IPrintable<T>और संचालित कर सकता है , जो आपको बेस क्लास के सभी कथित लाभों को एक विधि के साथ देता है और सामान्य एसआरपी उल्लंघनों में से कोई भी नहीं। । आईओसी कंटेनर पंजीकरण में वास्तविक कार्यान्वयन की आवश्यकता केवल एक बार घोषित की जानी चाहिए।Reportprint

कुछ लोग, जब उपरोक्त डिज़ाइन के साथ सामना किया जाता है, तो कुछ इस तरह से जवाब देते हैं: "लेकिन यह प्रक्रियात्मक कोड जैसा दिखता है, और OOP का पूरा बिंदु हमें-प्राप्त करना था- डेटा और व्यवहार के पृथक्करण से!" जिसको मैं कहता हूं: गलत

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

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

लेकिन formatऔर printतरीकों, वे जानकारी के साथ ही कुछ नहीं करना है। वास्तव में, यह बहुत कम संभावना नहीं है कि आप इन विधियों के कई कार्यान्वयन करना चाहते हैं , उदाहरण के लिए प्रबंधन के लिए एक विस्तृत विवरण और शेयरधारकों के लिए एक नहीं-तो-विस्तृत विवरण। इन स्वतंत्र कार्यों को अलग-अलग वर्गों में अलग-अलग करने से आपको एक-आकार-फिट print(bool includeDetails, bool includeSubtotals, bool includeTotals, int columnWidth, CompanyLetterhead letterhead, ...)विधि के बोझ के बिना रनटाइम पर अलग-अलग कार्यान्वयन का चयन करने की क्षमता मिलती है । नीरस

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

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


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


+1 महान जवाब वास्तव में। हालाँकि, मैं सिर्फ क्लास को लेकर उलझन में हूँ IncomeStatement। क्या आपके प्रस्तावित डिजाइन का मतलब है कि IncomeStatementइच्छाशक्ति के उदाहरण हैं IncomeStatementPrinterऔर IncomeStatementRendererइसलिए जब मैं इस print()पर कॉल IncomeStatementकरूंगा तो कॉल को IncomeStatementPrinterइसके बजाय सौंप देंगे ?
सांगो

@Songo: बिल्कुल नहीं! यदि आप SOLID का अनुसरण कर रहे हैं, तो आपके पास चक्रीय निर्भरता नहीं होनी चाहिए। जाहिरा तौर पर मेरे जवाब ने यह स्पष्ट नहीं किया कि IncomeStatementकक्षा में एक printविधि या एक formatविधि या कोई अन्य विधि नहीं है जो सीधे रिपोर्ट डेटा का निरीक्षण या हेरफेर नहीं करती है। यही उन अन्य वर्गों के लिए है। यदि आप एक प्रिंट करना चाहते हैं, तो आप उस IPrintable<IncomeStatement>इंटरफ़ेस पर निर्भरता लेते हैं जो कंटेनर में पंजीकृत है।
आरोनोटेड

मैं तुम्हारी बात देख रहा हूं। हालांकि, अगर मैं कक्षा में एक उदाहरण इंजेक्ट करता हूं तो चक्रीय निर्भरता कहां है ? जिस तरह से मैं इसकी कल्पना करता हूं, जब मैं कहता हूं कि यह इसे सौंप देगा । इस दृष्टिकोण के साथ क्या गलत है? ... एक अन्य प्रश्न, आपने उल्लेख किया है कि इसमें वह जानकारी होनी चाहिए जो स्वरूपित रिपोर्टों पर दिखाई देती है यदि मैं चाहता हूं कि इसे डेटाबेस से या एक्सएमएल फ़ाइल से पढ़ा जाए, तो क्या मुझे उस विधि को निकालना चाहिए जो डेटा को लोड करती है एक अलग वर्ग में और यह करने के लिए कॉल प्रतिनिधि ? PrinterIncomeStatementIncomeStatement.print()IncomeStatementPrinter.print(this, format)IncomeStatementIncomeStatement
सांगो

@Songo: आप पर IncomeStatementPrinterनिर्भर करता है IncomeStatementऔर IncomeStatementनिर्भर करता है IncomeStatementPrinter। यह एक चक्रीय निर्भरता है। और यह सिर्फ खराब डिजाइन है; इसके IncomeStatementबारे में कुछ भी जानने का कोई कारण नहीं है - Printerया IncomeStatementPrinterयह एक डोमेन मॉडल है, यह मुद्रण से संबंधित नहीं है, और प्रतिनिधिमंडल बेकार है क्योंकि कोई भी अन्य वर्ग ए या बना सकता है IncomeStatementPrinter। डोमेन मॉडल में मुद्रण की कोई धारणा होने का कोई अच्छा कारण नहीं है।
एरोन

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

2

जिस तरह से मैं एसआरपी के लिए जांच करता हूं वह एक कक्षा के प्रत्येक तरीके (जिम्मेदारी) की जांच करना है और निम्नलिखित प्रश्न पूछें:

"क्या मुझे कभी इस समारोह को लागू करने के तरीके को बदलने की आवश्यकता होगी?"

अगर मुझे कोई ऐसा फ़ंक्शन मिलता है, जिसे मुझे अलग-अलग तरीकों से लागू करने की आवश्यकता होगी (कुछ प्रकार के कॉन्फ़िगरेशन या स्थिति के आधार पर) तो मुझे यकीन है कि मुझे इस जिम्मेदारी को संभालने के लिए एक अतिरिक्त वर्ग की आवश्यकता है।


1

यहाँ वस्तु Calisthenics के नियम 8 से एक उद्धरण है :

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

इस (कुछ आदर्शवादी) दृष्टिकोण को देखते हुए, आप कह सकते हैं कि किसी भी वर्ग में केवल एक या दो राज्य चर होते हैं, जो एसआरपी का उल्लंघन करने की संभावना नहीं है। आप यह भी कह सकते हैं कि किसी भी वर्ग में दो से अधिक राज्य चर शामिल हैं जो SRP का उल्लंघन कर सकते हैं।


2
यह दृश्य आशातीत सरल है। यहां तक ​​कि आइंस्टीन के प्रसिद्ध, लेकिन सरल समीकरण के लिए दो चर की आवश्यकता होती है।
रॉबर्ट हार्वे

OPs सवाल था "क्या SRP की जांच करने के और तरीके हैं?" - यह एक संभव संकेतक है। हां यह सरल है, और यह हर मामले में पकड़ में नहीं आता है, लेकिन यह जांचने का एक संभव तरीका है कि SRP का उल्लंघन किया गया है।
मैटवेवी

1
मुझे संदेह है कि अपरिवर्तनीय बनाम अपरिवर्तनीय अवस्था भी एक महत्वपूर्ण विचार है
jk।

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

@ डंक मैं आपसे असहमत नहीं हूं, लेकिन यह चर्चा सवाल के लिए पूरी तरह से विषय है।
मैटडावे

1

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

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

public class Report
{
    private ReportData data;
    private ReportDataDao dao;
    private ReportFormatter formatter;
    private ReportPrinter printer;


    /*
     *  Parameterized constructor for depndency injection, 
     *  there are better ways but this is explicit.
     */
    public Report(ReportDataDao dao, 
        ReportFormatter formatter, ReportPrinter printer)
    {
        super();
        this.dao = dao;
        this.formatter = formatter;
        this.printer = printer;
    }

    /*
     * Delegates to the injected printer.
     */
    public void printReport()
    {
        printer.print(formatReport());
    }


    /*
     * Lazy loading of data, delegates to the dao 
     * for the meat of the call.
     */
    public ReportData getReportData()
    {
        if (reportData == null)
        {
            reportData = dao.loadData();
        }
        return reportData;
    }

    /*
     * Delegate to the formatter for formatting 
     * (notice a pattern here).
     */
    public ReportData formatReport()
    {
        formatter.format(getReportData());
    }
}

कार्यान्वयन के लिए धन्यवाद। मेरे पास 2 चीजें हैं, लाइन में if (reportData == null)मैं आपको dataइसके बजाय मतलब है । दूसरी बात, मैं यह जानना चाह रहा था कि आप इस क्रियान्वयन में कैसे पहुंचे। जैसे आपने इसके बजाय सभी कॉल को अन्य ऑब्जेक्ट्स पर डेलिगेट करने का निर्णय क्यों लिया। एक और बात है कि मैं हमेशा के बारे में सोच रहा था, क्या यह वास्तव में एक रिपोर्ट की जिम्मेदारी है खुद को मुद्रित करने के लिए ?! आपने एक अलग printerवर्ग क्यों नहीं बनाया जो reportइसके निर्माता में है?
सांगो

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

हम्मम ... तो अगर आप स्क्रैच से सिस्टम बना रहे हैं, तो आप कौन सा विकल्प लेंगे? एक Printerवर्ग जो एक रिपोर्ट लेता है या एक Reportवर्ग जो एक प्रिंटर लेता है? मुझे पहले भी इसी तरह की समस्या का सामना करना पड़ा है, जहां मुझे एक रिपोर्ट को पार्स करना था और मैंने अपने टीएल के साथ तर्क दिया कि अगर हमें एक ऐसा पार्सर बनाना चाहिए जो रिपोर्ट लेता है या क्या रिपोर्ट में इसके अंदर एक पार्सर होना चाहिए और parse()कॉल इसके लिए प्रत्यायोजित है।
सांगो

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

0

आपके उदाहरण में, यह स्पष्ट नहीं है कि एसआरपी का उल्लंघन किया जा रहा है। हो सकता है कि यदि वे अपेक्षाकृत सरल हैं, तो रिपोर्ट को प्रारूपित और मुद्रित करने में सक्षम होना चाहिए:

class Report {
  void format() {
     text = text.trim();
  }

  void print() {
     new Printer().write(text);
  }
}

विधियाँ इतनी सरल हैं कि इसका कोई मतलब नहीं है ReportFormatterया ReportPrinterकक्षाएं हैं। इंटरफ़ेस में एकमात्र चकाचौंध की समस्या है, getReportDataक्योंकि यह उल्लंघन करता है पूछें गैर-मूल्य ऑब्जेक्ट पर न बताएं।

दूसरी ओर, यदि विधियाँ बहुत जटिल हैं या Reportफिर प्रारूप बनाने या मुद्रित करने के कई तरीके हैं, तो यह जिम्मेदारी सौंपने के लिए समझ में आता है (और भी परीक्षण योग्य):

class Report {
  void format(ReportFormatter formatter) {
     text = formatter.format(text);
  }

  void print(ReportPrinter printer) {
     printer.write(text);
  }
}

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

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

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


क्या आप मेरे द्वारा पोस्ट से जुड़े उदाहरण की जांच कर सकते हैं और इसके आधार पर अपने उत्तर को विस्तृत कर सकते हैं।
सांगो

अपडेट किया गया। एसआरपी संदर्भ पर निर्भर करता है, यदि आपने एक पूरी कक्षा (एक अलग प्रश्न में) पोस्ट की है तो यह समझाना आसान होगा।
गैरेट हॉल

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

मैं सिर्फ इतना कह रहा हूं कि SRP उस कोड पर निर्भर करता है जो आपको इसे हठधर्मिता से लागू नहीं करना चाहिए।
गैरेट हॉल

हाँ, मैं आपकी बात समझ गया। लेकिन अगर आप स्क्रैच से सिस्टम बना रहे हैं, तो आप कौन सा विकल्प लेंगे? एक Printerवर्ग जो एक रिपोर्ट लेता है या एक Reportवर्ग जो एक प्रिंटर लेता है? कई बार मुझे इस तरह के डिजाइन प्रश्न का सामना करना पड़ता है कि यह पता लगाने से पहले कि कोड जटिल साबित होगा या नहीं।
सांगो

0

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

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

यदि आपके तरीके सभी चर का उपयोग नहीं करते हैं, तो भी सामंजस्य पर वापस आना, उन्हें युग्मित किया जाना चाहिए:

public class MyClass {
    private Type1 var1;
    private Type2 var2;
    private Type3 var3;

    public Type3 method1() {
        //use var1 and var3
    }  

    public void method2() {
        //use var1 and var2
    }

    public Type1 method3() {
        //use var2 and var3
    }
}

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

public class MyClass {
    private Type1 var1;
    private Type2 var2;
    private Type3 var3;
    private TypeA varA;
    private TypeB varB;

    public Type3 method1() {
        //use var1 and var3
    }  

    public void method2() {
        //use var1 and var2
    }

    public TypeA methodA() {
        //use varA and varB
    }

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