एक से अधिक क्लाइंट के लिए एक ही सॉफ्टवेयर के अलग-अलग, कस्टमाइज्ड वर्जन कैसे बनाए रखें


46

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

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

क्या आप बिल्ड सिस्टम को कई बिल्ड बनाने की सलाह देंगे? मुझे विशेष रूप से स्रोत नियंत्रण, svn में विभिन्न अनुकूलन कैसे संग्रहीत करने चाहिए?


4
#ifdefआपके लिए काम करता है ?
ओहो

6
प्रीप्रोसेसर निर्देश जल्दी से बहुत जटिल हो सकते हैं और कोड को पढ़ने के लिए कठिन और डिबग के लिए कठिन बना सकते हैं।
फॉल्कन

1
आपको बेहतर उत्तरों के लिए वास्तविक प्लेटफ़ॉर्म और एप्लिकेशन के प्रकार (डेस्कटॉप / वेब) के बारे में विवरण शामिल करना चाहिए। डेस्कटॉप C ++ ऐप में कस्टमाइज़ करना PHP वेब ऐप की तुलना में पूरी तरह से अलग है।
ग्रैंडमास्टरबी

2
@ फाल्कन: चूंकि आपने 2011 में एक उत्तर दिया था जिसके बारे में मुझे बहुत संदेह है, तो क्या आप हमें बता सकते हैं कि क्या आपको बीच में सुझाए गए तरीके से एसवीएन का उपयोग करने का कोई अनुभव मिला है? क्या मेरी आपत्तियां बीमार हैं?
डॉक्टर ब्राउन

2
@ डॉक ब्राउन: ब्रांचिंग और मर्जिंग थकाऊ और जटिल थी। इसके बाद, हमने क्लाइंट विशिष्ट प्लगइन्स या "पैच" के साथ एक प्लगइन सिस्टम का उपयोग किया जो व्यवहार या कॉन्फ़िगरेशन को बदल देता है। आपके पास कुछ ओवरहेड होगा, लेकिन इसे डिपेंडेंडी इंजेक्शन के साथ प्रबंधित किया जा सकता है।
फाल्कन

जवाबों:


7

आपको जो चाहिए वह है सुविधा शाखा -समान कोड संगठन। आपके विशिष्ट परिदृश्य में इसे क्लाइंट-विशिष्ट ट्रंक ब्रांचिंग कहा जाना चाहिए क्योंकि जब आप नए सामान को विकसित करने (या बग को हल करने) के साथ-साथ फ़ीचर ब्रांचिंग का उपयोग करेंगे।

http://svnbook.red-bean.com/en/1.5/svn.branchmerge.commonpatterns.html

इस विचार में एक कोड ट्रंक है जिसमें नई सुविधाओं की शाखाओं का विलय किया गया है। मेरा मतलब सभी गैर-क्लाइंट-विशिष्ट सुविधाओं से है।

फिर आपके पास आपकी क्लाइंट-विशिष्ट शाखाएँ भी हैं जहाँ आप आवश्यक रूप से उसी विशेषताओं की शाखाओं को मिलाते हैं (यदि आप चाहें तो चेरी-पिकिंग)।

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

क्लाइंट-विशिष्ट शाखाएं ट्रंक के समानांतर शाखाएं हैं और ट्रंक के रूप में लंबे समय से सक्रिय हैं और कहीं भी पूरे रूप में विलय नहीं किया जाता है।

            feature1
            ———————————.
                        \
trunk                    \
================================================== · · ·
      \ client1            \
       `========================================== · · ·
        \ client2            \
         `======================================== · · ·
              \ client2-specific feature   /
               `——————————————————————————´

7
सुविधा शाखा के लिए +1। आप प्रत्येक क्लाइंट के लिए भी एक शाखा का उपयोग कर सकते हैं। मैं सिर्फ एक बात का सुझाव देना चाहता हूं: इसे पूरा करने के लिए एसवीएन / सीवीएस के बजाय एक वितरित वीसीएस (एचजी, जीआईटी, बीजीआर) का उपयोग करें;)
हर्बर्ट अमरल

43
-1। वह यह नहीं है कि "सुविधा शाखाएं" किसके लिए हैं; यह उनकी परिभाषा को "अस्थायी शाखाओं के रूप में विरोधाभासी करता है जो किसी विशेषता के विकास के समाप्त होने पर विलीन हो जाती है"।
पी शेव्ड

14
आपको अभी भी उन सभी डेल्टास को स्रोत नियंत्रण में रखना होगा और उन्हें हमेशा के लिए पंक्तिबद्ध रखना होगा। अल्पावधि यह काम कर सकता है। लंबे समय तक यह भयानक हो सकता है।
जल्दी_अगला

4
-1, यह एक नामकरण समस्या नहीं है - विभिन्न ग्राहकों के लिए विभिन्न शाखाओं का उपयोग करना कोड डुप्लिकेट का एक और रूप है। यह IMHO एक विरोधी पैटर्न है, एक उदाहरण है कि यह कैसे नहीं करना है।
डॉक ब्राउन

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

38

एससीएम शाखाओं के साथ ऐसा न करें। कॉमन कोड को एक अलग प्रोजेक्ट बनाते हैं जो लाइब्रेरी या प्रोजेक्ट कंकाल की कलाकृति तैयार करता है। प्रत्येक ग्राहक परियोजना एक अलग परियोजना है जो तब एक निर्भरता के रूप में आम पर निर्भर करती है।

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


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

अगर मुझे कुछ याद नहीं है, तो आप जो सुझाव देते हैं वह यह है कि यदि आपके पास 100 ग्राहक हैं, तो आप (100 x नंबरऑफचेंजप्रोजेक्ट्स ) बनाएंगे और उन्हें प्रबंधित करने के लिए DI का उपयोग करेंगे? यदि ऐसा है, तो मैं निश्चित रूप से इस तरह के समाधान से दूर
रहूंगा क्योंकि स्थिरता

13

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

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

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

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


8

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


5

बहूत सावधानी से

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

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

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

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

तो संक्षेप में:

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

यदि ठीक से किया जाए तो आपके उत्पाद की असेंबली में सभी लेकिन कुछ विन्यास फाइल होनी चाहिए।

थोड़ी देर के लिए इसका उपयोग करने के बाद मैंने मेटा-पैकेज बनाना शुरू किया जो कि कोर यूनिट के रूप में ज्यादातर उपयोग किए गए या आवश्यक सिस्टम को इकट्ठा करता है और ग्राहक असेंबलियों के लिए इस मेटा-पैकेज का उपयोग करता है। कुछ वर्षों के बाद मैंने एक बड़ा टूलबॉक्स तैयार किया जिसे मैं ग्राहक समाधान बनाने के लिए बहुत जल्दी इकट्ठा कर सकता था। मैं वर्तमान में स्प्रिंग रूओ में देख रहा हूं और देख सकता हूं कि क्या मैं इस विचार को थोड़ा और आगे नहीं बढ़ा सकता हूं कि एक दिन मैं अपने पहले साक्षात्कार में ग्राहक के साथ सिस्टम का पहला मसौदा बना सकता हूं ... मुझे लगता है कि आप इसे उपयोगकर्ता के लिए प्रेरित कर सकते हैं। विकास ;-)।

उम्मीद है कि इससे मदद मिली


3

परिवर्तनों को संभवतः प्रत्येक ग्राहक के लिए मॉड्यूल को एक अलग (भौतिक) मॉड्यूल में विभाजित करने का औचित्य साबित करने के लिए बहुत छोटा है, मुझे बिल्ड, लिंकिंग अराजकता के साथ समस्याओं का डर है।

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

यदि आपके मॉड्यूल को इस तरह कोडित किया जाता है कि मॉड्यूल A में नीति X1 का उपयोग करने के लिए मॉड्यूल B में नीति X2 का उपयोग करने की आवश्यकता होती है, तो रिफैक्टिंग के बारे में सोचें ताकि X1 और X2 को एकल नीति वर्ग में जोड़ा जा सके।


1

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


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


1

यदि आप सादे सी में लिख रहे हैं, तो यह करने के लिए एक बदसूरत तरीका है।

  • सामान्य कोड (जैसे इकाई "frangulator.c")

  • ग्राहक-विशिष्ट कोड, टुकड़े जो छोटे होते हैं और केवल प्रत्येक ग्राहक के लिए उपयोग किए जाते हैं।

  • मुख्य इकाई कोड में, ऐसा करने के लिए #ifdef और #include का उपयोग करें

#ifdef CLIENT = CLIENTA
#include "frangulator_client_a.c"
#अगर अंत

सभी कोड इकाइयों में क्लाइंट-विशिष्ट अनुकूलन की आवश्यकता होती है और इसे एक पैटर्न के रूप में उपयोग करें।

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

इसका यह भी अर्थ है कि सभी क्लाइंट-विशिष्ट टुकड़े हर समय (प्रत्येक फ़ाइल में प्रत्येक) स्पष्ट रूप से दिखाई देते हैं, और मुख्य कोड फ़ाइल और फ़ाइल के क्लाइंट-विशिष्ट भाग के बीच एक स्पष्ट संबंध है।

यदि आप वास्तव में चतुर हैं, तो आप सही क्लाइंट परिभाषा बनाने के लिए मेकफाइल्स सेट कर सकते हैं, जैसे कुछ:

ग्राहक बनाओ

client_a के लिए निर्माण करेगा, और "clientb बनाओ" client_b और इतने पर बना देगा।

(और "दिया" बिना किसी लक्ष्य के चेतावनी या उपयोग विवरण जारी कर सकता है।)

मैंने पहले भी एक समान विचार का उपयोग किया है, इसे स्थापित करने में थोड़ा समय लगता है लेकिन यह बहुत प्रभावी हो सकता है। मेरे मामले में एक स्रोत के पेड़ ने लगभग 120 विभिन्न उत्पादों का निर्माण किया।


0

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

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

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