ठोस सिद्धांतों को लागू करना


13

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

एप्लिकेशन में निम्न कार्य है (वास्तव में इससे बहुत अधिक है लेकिन इसे सरल रखें): इसे एक XML फ़ाइल को पढ़ना है जिसमें डेटाबेस टेबल / कॉलम / व्यू आदि की परिभाषाएँ हैं और एक SQL फ़ाइल बना सकते हैं जिसे बनाने के लिए उपयोग किया जा सकता है एक ORACLE डेटाबेस स्कीमा।

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

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

एक बाधा एक तालिका का हिस्सा है (या अधिक सटीक, एक रचनात्मक तालिका का हिस्सा), और एक बाधा भी एक अन्य तालिका को संदर्भित कर सकती है।

सबसे पहले, मैं समझाता हूं कि आवेदन अभी क्या दिखता है (SOLID लागू नहीं):

फिलहाल, एप्लिकेशन में एक "तालिका" वर्ग है जिसमें तालिका के स्वामित्व वाले बिंदुओं की सूची और इस तालिका को संदर्भित करने वाली बाधाओं की एक सूची है। जब भी कोई कनेक्शन स्थापित होता है, तो बैकवर्ड कनेक्शन भी स्थापित हो जाएगा। तालिका में एक createStatement () विधि है जो बदले में प्रत्येक बाधा के createStatement () फ़ंक्शन को कॉल करती है। कहा विधि स्वयं उनके नाम को पुनः प्राप्त करने के लिए कनेक्शन का उपयोग स्वामी तालिका और संदर्भित तालिका में करेगी।

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

तो कुछ सवाल हैं:

  1. क्या मुझे निर्भरता इंजेक्शन का उपयोग करके परिपत्र निर्भरता को हल करना चाहिए? यदि ऐसा है, तो मुझे लगता है कि बाधा को इसके निर्माण में मालिक (और वैकल्पिक रूप से संदर्भित) तालिका प्राप्त होनी चाहिए। लेकिन मैं तब एक ही मेज के लिए बाधाओं की सूची पर कैसे दौड़ सकता था?
  2. यदि तालिका वर्ग दोनों स्वयं की स्थिति (उदाहरण के लिए तालिका का नाम, तालिका टिप्पणी आदि) और बाधाओं को जोड़ता है, तो क्या ये एकल जिम्मेदारी सिद्धांत के बारे में सोच रहे हैं?
  3. मामले में 2. सही है, क्या मुझे तार्किक व्यापार परत में एक नया वर्ग बनाना चाहिए जो लिंक का प्रबंधन करता है? यदि हां, तो 1. स्पष्ट रूप से प्रासंगिक नहीं रह जाएगा।
  4. क्या "createStatement" विधियों को तालिका / बाधा कक्षाओं का हिस्सा होना चाहिए या क्या मुझे उन्हें भी स्थानांतरित करना चाहिए? यदि हां, तो कहां करें? प्रत्येक डेटा भंडारण वर्ग (यानी टेबल, बाधा, ...) प्रति एक प्रबंधक वर्ग? या प्रति लिंक प्रबंधक श्रेणी बनाएं (3. के समान)?

जब भी मैं इनमें से किसी एक सवाल का जवाब देने की कोशिश करता हूं, तो मैं खुद को हलकों में दौड़ता हुआ पाता हूं।

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


3
आप किस भाषा का उपयोग कर रहे हैं? क्या आप कम से कम कुछ कंकाल कोड पोस्ट कर सकते हैं? वास्तविक कोड को देखे बिना कोड गुणवत्ता और संभावित रिफलेक्टरिंग पर चर्चा करना बहुत कठिन है।
Péter Török

मैं सी ++ का उपयोग कर रहा हूं, लेकिन मैं इसे चर्चा से बाहर रखने की कोशिश कर रहा हूं क्योंकि आपको किसी भी भाषा में यह समस्या हो सकती है
टिम मेयर

हां, लेकिन पैटर्न और रिफ्लेक्टरिंग का अनुप्रयोग भाषा पर निर्भर है। उदाहरण के लिए @ back2dos ने नीचे दिए गए अपने उत्तर में AOP का सुझाव दिया, जो स्पष्ट रूप से C ++ पर लागू नहीं होता है।
पैटर टॉर्क

कृपया SOLID सिद्धांतों पर अधिक जानकारी के लिए प्रोग्रामर.स्टैकएक्सचेंज.
com

जवाबों:


8

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

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

[XML] -> ("Read XML") -> [Data model of DB definition] -> ("Write SQL") -> [SQL]

तो एकमात्र स्थान जहां XML विशिष्ट कोड रखा जाना चाहिए, उदाहरण के लिए, नाम का एक वर्ग है Read_XML। SQL विशिष्ट कोड के लिए एकमात्र स्थान एक वर्ग जैसा होना चाहिए Write_SQL। बेशक, शायद आप उन 2 कार्यों को अधिक उप-कार्यों में विभाजित करने जा रहे हैं (और अपनी कक्षाओं को कई प्रबंधक वर्गों में विभाजित करें), लेकिन आपके "डेटा मॉडल" को उस परत से कोई जिम्मेदारी नहीं लेनी चाहिए। इसलिए createStatementअपने डेटा मॉडल वर्गों में से किसी को भी न जोड़ें , क्योंकि यह आपके डेटा मॉडल को SQL के लिए ज़िम्मेदारी देता है।

जब आप वर्णन कर रहे हैं कि मुझे कोई समस्या नहीं दिख रही है, तो एक तालिका उसके सभी भागों, (नाम, कॉलम, टिप्पणियाँ, बाधाएं ...) को पकड़ने के लिए जिम्मेदार है, जो कि एक डेटा मॉडल के पीछे का विचार है। लेकिन आपने वर्णित किया "टेबल" इसके कुछ हिस्सों के स्मृति प्रबंधन के लिए भी जिम्मेदार है। यह एक सी ++ विशिष्ट मुद्दा है, जिसे आप जावा या सी # जैसी भाषाओं में आसानी से सामना नहीं करेंगे। उन जिम्मेदारी से मुक्त होने का C ++ तरीका स्मार्ट पॉइंटर्स का उपयोग कर रहा है, स्वामित्व को एक अलग परत (उदाहरण के लिए, बूस्ट लाइब्रेरी या आपके अपने "स्मार्ट" पॉइंटर लेयर) के लिए स्वामित्व का उपयोग कर रहा है। लेकिन सावधान रहें, आपकी चक्रीय निर्भरता कुछ स्मार्ट पॉइंटर कार्यान्वयन को "इरिटेट" कर सकती है।

SOLID के बारे में कुछ और: यहाँ अच्छा लेख है

http://cre8ivethought.com/blog/2011/08/23/software-development-is-not-a-jenga-game

एक छोटे से उदाहरण द्वारा SOLID की व्याख्या करना। आइए इसे अपने मामले में लागू करने का प्रयास करें:

  • आपको न केवल कक्षाओं Read_XMLऔर Write_SQL, बल्कि तीसरी कक्षा की भी आवश्यकता होगी , जो उन 2 वर्गों के आपसी संपर्क का प्रबंधन करती है। चलो इसे एक कहते हैं ConversionManager

  • DI सिद्धांत को लागू करने का मतलब यहां हो सकता है: कनवर्ज़न मैनजर को Read_XMLऔर Write_SQLस्वयं के उदाहरण नहीं बनाने चाहिए । इसके बजाय, उन वस्तुओं को कंस्ट्रक्टर के माध्यम से इंजेक्ट किया जा सकता है। और निर्माणकर्ता के पास इस तरह का एक हस्ताक्षर होना चाहिए

    ConversionManager(IDataModelReader reader, IDataModelWriter writer)

जहाँ IDataModelReaderएक इंटरफ़ेस है जहाँ से Read_XMLविरासत में मिला है, और IDataModelWriterउसी के लिए Write_SQL। यह ConversionManagerएक्सटेंशन के लिए एक खुला बनाता है (आप बहुत आसानी से विभिन्न पाठकों या लेखकों को प्रदान करते हैं) इसे बदलने के बिना - इसलिए हमारे पास ओपन / बंद सिद्धांत के लिए एक उदाहरण है। जब आप किसी अन्य डेटाबेस विक्रेता का समर्थन करना चाहते हैं, तो आपको इसके बारे में सोचना होगा।


हालांकि यह SOLID, (upvoted) नोट का एक बहुत ही उचित अभ्यास है, यह एक पुराने एनेमिक डेटा मॉडल के लिए गेटर्स और सेटर्स की आवश्यकता के द्वारा "पुराने स्कूल के / होलूब ओओपी" का उल्लंघन करता है। यह मुझे कुख्यात स्टीव येजेग रैंट की भी याद दिलाता है ।
उपयोगकर्ता 949300

2

ठीक है, आपको इस मामले में एसओआईएल के एस को लागू करना चाहिए।

एक तालिका इस पर परिभाषित सभी बाधाओं को रखती है। एक बाधा सभी तालिकाओं को संदर्भित करती है। सादा और सरल मॉडल।

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

एक बहुत ही सरल संस्करण के लिए इसे तोड़ने के लिए:

class Table {
      void addConstraint(Constraint constraint) { ... }
      bool removeConstraint(Constraint constraint) { ... }
      Iterator<Constraint> getConstraints() { ... }
}
class Constraint {
      //actually I am not so sure these two should be exposed directly at all
      void addReference(Table to) { ... }
      bool removeReference(Table to) { ... }
      Iterator<Table> getReferencedTables() { ... }
}
class Database {
      void addTable(Table table) { ... }
      bool removeTable(Table table) { ... }
      Iterator<Table> getTables() { ... }
}
class Index {
      Iterator<Constraint> getConstraintsReferencing(Table target) { ... }
}

सूचकांक के कार्यान्वयन के लिए, वहाँ जाने के लिए 3 तरीके हैं:

  • getContraintsReferencingविधि वास्तव में सिर्फ पूरे क्रॉल कर सकता है Databaseके लिए Tableउदाहरण हैं और उनके क्रॉल Constraintरों परिणाम प्राप्त करने के। यह कितना महंगा है और कितनी बार आपको इसकी आवश्यकता है, इसके आधार पर यह एक विकल्प हो सकता है।
  • यह भी एक कैश का उपयोग कर सकता है। यदि आपका डेटाबेस मॉडल एक बार परिभाषित होने के बाद बदल सकता है, तो आप संबंधित Tableऔर Constraintउदाहरणों से संकेतों को फायर करके कैश को बनाए रख सकते हैं , जब वे बदलते हैं। थोड़ा सरल उपाय यह होगा कि आप काम करने Indexके लिए पूरी तरह Databaseसे "स्नैपशॉट इंडेक्स" का निर्माण करें, ताकि आप इसे छोड़ दें। यह केवल तभी संभव है, यदि आपका आवेदन "मॉडलिंग समय" और "क्वेरी टाइम" के बीच एक बड़ा अंतर रखता है। यदि यह एक ही समय में उन दोनों को करने की संभावना है, तो यह व्यवहार्य नहीं है।
  • एक अन्य विकल्प यह होगा कि AOP का उपयोग पूरी रचना कॉल को इंटरसेप्ट करने और इंडेक्स को उसी के अनुसार बनाए रखने के लिए किया जाए।

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

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

1

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

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

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


यह वह जगह है जहां "(वास्तव में इससे बहुत अधिक है लेकिन इसे सरल रखें)" खेलने में आता है। उदाहरण के लिए, ऐसे मामले हैं जहां मुझे एक तालिका को हटाने की आवश्यकता है, इसलिए मुझे यह जांचने की आवश्यकता है कि क्या कोई बाधा इस तालिका को संदर्भित कर रही है।
टिम मेयर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.