डुप्लिकेट कोड को हटाने के लिए जटिलता जोड़ना


24

मेरे पास कई वर्ग हैं जो सभी एक सामान्य आधार वर्ग से प्राप्त करते हैं। बेस क्लास में कई प्रकार की वस्तुओं का संग्रह होता है T

प्रत्येक बाल वर्ग को वस्तुओं के संग्रह से प्रक्षेपित मूल्यों की गणना करने में सक्षम होने की आवश्यकता होती है, लेकिन चूंकि बाल वर्ग विभिन्न प्रकारों का उपयोग करते हैं, इसलिए गणना कक्षा से कक्षा में एक छोटे से भिन्न होती है।

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

मुझे लगता है कि DRY सिद्धांत इस तरह की स्थिति में उतना लागू नहीं होता है, लेकिन यह ईशनिंदा जैसा लगता है। कोड डुप्लीकेशन को हटाने की कोशिश में कितनी जटिलता है?

संपादित करें:

सबसे अच्छा समाधान मैं इस तरह से कुछ के साथ आ सकते हैं:

बेस क्लास:

protected T GetInterpolated(int frame)
{
    var index = SortedFrames.BinarySearch(frame);
    if (index >= 0)
        return Data[index];

    index = ~index;

    if (index == 0)
        return Data[index];
    if (index >= Data.Count)
        return Data[Data.Count - 1];

    return GetInterpolatedItem(frame, Data[index - 1], Data[index]);
}

protected abstract T GetInterpolatedItem(int frame, T lower, T upper);

बाल वर्ग ए:

public IGpsCoordinate GetInterpolatedCoord(int frame)
{
    ReadData();
    return GetInterpolated(frame);
}

protected override IGpsCoordinate GetInterpolatedItem(int frame, IGpsCoordinate lower, IGpsCoordinate upper)
{
    double ratio = GetInterpolationRatio(frame, lower.Frame, upper.Frame);

    var x = GetInterpolatedValue(lower.X, upper.X, ratio);
    var y = GetInterpolatedValue(lower.Y, upper.Y, ratio);
    var z = GetInterpolatedValue(lower.Z, upper.Z, ratio);

    return new GpsCoordinate(frame, x, y, z);
}

बाल वर्ग बी:

public double GetMph(int frame)
{
    ReadData();
    return GetInterpolated(frame).MilesPerHour;
}

protected override ISpeed GetInterpolatedItem(int frame, ISpeed lower, ISpeed upper)
{
    var ratio = GetInterpolationRatio(frame, lower.Frame, upper.Frame);
    var mph = GetInterpolatedValue(lower.MilesPerHour, upper.MilesPerHour, ratio);
    return new Speed(frame, mph);
}

9
DRY और कोड पुन: उपयोग जैसी अवधारणाओं का एक अति सूक्ष्म अनुप्रयोग, अधिक से अधिक पापों की ओर ले जाता है।
Affe

1
आपको कुछ अच्छे सामान्य उत्तर मिल रहे हैं। उदाहरण फ़ंक्शन को शामिल करने का संपादन हमें यह निर्धारित करने में मदद कर सकता है कि क्या आप इसे इस विशेष उदाहरण में बहुत दूर ले जा रहे हैं।
कार्ल बेज़ेलफेल्ट

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

1
पहली बात यह है कि ट्रिपल एक्स, वाई, जेड को एक स्थिति प्रकार में इकट्ठा करना, और उस प्रकार के लिए प्रक्षेप को एक सदस्य या शायद एक स्थिर विधि के रूप में जोड़ना: स्थिति प्रक्षेप (स्थिति अन्य, अनुपात)।
केविन क्लाइन

जवाबों:


30

एक तरह से, आपने अंतिम पैराग्राफ में उस टिप्पणी के साथ अपने प्रश्न का उत्तर दिया:

मुझे लगता है कि DRY सिद्धांत इस तरह की स्थिति में उतना लागू नहीं होता है, लेकिन यह ईशनिंदा जैसा लगता है

जब भी आप अपनी समस्या को हल करने के लिए कुछ अभ्यास वास्तव में व्यावहारिक नहीं पाते हैं, तो उस अभ्यास का धार्मिक रूप से उपयोग करने का प्रयास न करें (शब्द ईशनिंदा इसके लिए चेतावनी की तरह है)। अधिकांश प्रथाओं उनके है whens और whys और भले ही वे सभी संभव मामलों के 99% को कवर किया है, वहाँ कि 1% जहां एक अलग दृष्टिकोण की आवश्यकता हो सकती अभी भी है।

विशेष रूप से, DRY के संबंध में , मैंने यह भी पाया कि कभी-कभी यह डुप्लिकेटेड लेकिन सरल के कई टुकड़ों के लिए वास्तव में बेहतर होता है एक विशाल राक्षसी की तुलना कोड के आपको देखते समय बीमार महसूस करते हैं।

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

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


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

@ सविश दिलचस्प बिंदु। इसके बारे में कभी इस तरह से नहीं सोचा था।
फिल

17

बाल वर्ग विभिन्न प्रकारों का उपयोग करते हैं, गणना कक्षा से कक्षा में एक छोटे से भिन्न होती है।

सबसे पहली और महत्वपूर्ण बात यह है: पहले स्पष्ट रूप से पहचान लें कि कौन सा हिस्सा बदल रहा है और कौन सा हिस्सा कक्षाओं के बीच नहीं बदल रहा है। एक बार आपने पहचान लिया कि, आपकी समस्या हल हो गई है।

रि-फैक्टरिंग शुरू करने से पहले पहले अभ्यास के रूप में करें। उसके बाद सब कुछ अपने आप घट जाएगा।


2
अच्छे से कहा। यह सिर्फ बार-बार होने वाले फंक्शन की समस्या हो सकती है।
कार्ल बेज़ेलफेल्ट

8

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

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

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

मैं बार-बार कोड तभी स्वीकार करूंगा जब यह छोटा, स्थिर, और रिफैक्टिंग सिर्फ बदसूरत हो। मूल रूप से मैं लगभग सभी दोहराव का कारक हूं जब तक कि मैं जावा नहीं लिख रहा हूं।

अपने मामले के लिए एक विशिष्ट सिफारिश देना असंभव है क्योंकि आपने कोड पोस्ट नहीं किया है, या यहां तक ​​कि संकेत दिया है कि आप किस भाषा का उपयोग कर रहे हैं।


लुआ एक परिचित नहीं है।
डेडएमजी

@ डेडएमजी: विख्यात; बेझिझक संपादित करें। इसलिए हमने आपको वह सारी प्रतिष्ठा दी है।
केविन क्लाइन

5

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

इसके साथ, बेस क्लास एल्गोरिदम का प्रदर्शन करता है, और जब यह प्रत्येक सब क्लास के लिए भिन्नता की बात आती है, तो यह एक अमूर्त पद्धति की ओर झुक जाता है जिसे लागू करना सब क्लास की जिम्मेदारी है। उदाहरण के लिए Page_Load को कार्यान्वित करने के लिए अपने कोड के तरीके और ASP.NET पृष्ठ के बारे में सोचें।


4

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

आपकी गणना कितनी जटिल है, इस बात पर निर्भर करता है कि गणना का प्रत्येक "टुकड़ा" इस तरह एक आभासी तरीका क्यों नहीं हो सकता है

Public Class Fraction
{
     public virtual Decimal GetNumerator(params?)
     public virtual Decimal GetDenominator(params?)
     //Some concrete method to actually compute GetNumerator / GetDenominator
}

और जब आप गणना के तर्क में "मामूली बदलाव" करने की आवश्यकता होती है, तो व्यक्तिगत टुकड़ों को ओवरराइड करें।

(यह एक बहुत छोटा और बेकार उदाहरण है कि आप छोटे तरीकों को ओवरराइड करते समय बहुत अधिक कार्यक्षमता कैसे जोड़ सकते हैं)


3

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

हालाँकि, आप इस तरह के निन्दात्मक विचारों से सावधान रहने के लिए भी काफी सही हैं। इसे छोड़ने का निर्णय लेने से पहले अपने विकल्पों के माध्यम से सोचने की बहुत कोशिश करें।

उदाहरण के लिए, बेस क्लास के बजाय यूटिलिटी क्लास / मेथड में उस दोहराया कोड को रखना बेहतर होगा? प्राथमिकता पर वरीयता देखें ।


2

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

यदि आप उन प्रश्नों में से किसी एक का उत्तर दे सकते हैं, तो आपके पास संभावित डुप्लिकेट कोड छोड़ने के लिए एक मजबूत मामला है


0

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

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

// this code is similar to class x function b

टिप्पणी पर्याप्त होगी। समस्या सुलझ गयी।


0

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

कुछ स्थान हैं जहाँ Microsoft DRY सिद्धांतों के विरुद्ध गया है। उदाहरण के लिए, Microsoft ने स्पष्ट रूप से हतोत्साहित करने के तरीकों को एक पैरामीटर को स्वीकार कर लिया है जो यह दर्शाता है कि विफलता एक अपवाद को फेंकना चाहिए या नहीं। हालांकि यह सच है कि एक "विफलताओं के अपवाद" पैरामीटर एक विधि के "सामान्य उपयोग" एपीआई में बदसूरत है, ऐसे पैरामीटर उन मामलों में बहुत सहायक हो सकते हैं जहां एक कोशिश / करो विधि को अन्य कोशिश / करो विधियों से बना होना चाहिए। यदि कोई विफलता होने पर कोई बाहरी विधि अपवाद को फेंकना चाहती है, तो कोई भी आंतरिक विधि कॉल जो विफल हो जाती है, को एक अपवाद फेंकना चाहिए, जो बाहरी विधि प्रचारित कर सकती है। यदि बाहरी विधि अपवाद को फेंकने वाली नहीं है, तो भीतर वाला भी नहीं है। यदि किसी पैरामीटर का उपयोग कोशिश / करने के बीच अंतर करने के लिए किया जाता है, तब बाहरी विधि इसे आंतरिक विधि को पारित कर सकती है। अन्यथा, बाहरी विधि के लिए "कोशिश" विधियों को कॉल करना आवश्यक होगा जब इसे "कोशिश" के रूप में व्यवहार करना चाहिए, और "करना" विधियों को जब "व्यवहार" के रूप में माना जाता है।

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