बहुत जटिल विधि से बचें - साइक्लोमैटिक जटिलता


23

यह निश्चित नहीं है कि साइक्लोमैटिक जटिलता को कम करने के लिए इस पद्धति के बारे में कैसे जाना जाए। सोनार की रिपोर्ट 13 जबकि 10 की उम्मीद है। मुझे यकीन है कि इस पद्धति को छोड़ने में कोई बुराई नहीं है, हालांकि, यह सिर्फ मुझे चुनौती दे रहा है कि सोनार के शासन का पालन कैसे किया जाए। किसी भी विचार की बहुत सराहना की जाएगी।

 public static long parseTimeValue(String sValue) {

    if (sValue == null) {
        return 0;
    }

    try {
        long millis;
        if (sValue.endsWith("S")) {
            millis = new ExtractSecond(sValue).invoke();
        } else if (sValue.endsWith("ms")) {
            millis = new ExtractMillisecond(sValue).invoke();
        } else if (sValue.endsWith("s")) {
            millis = new ExtractInSecond(sValue).invoke();
        } else if (sValue.endsWith("m")) {
            millis = new ExtractInMinute(sValue).invoke();
        } else if (sValue.endsWith("H") || sValue.endsWith("h")) {
            millis = new ExtractHour(sValue).invoke();
        } else if (sValue.endsWith("d")) {
            millis = new ExtractDay(sValue).invoke();
        } else if (sValue.endsWith("w")) {
            millis = new ExtractWeek(sValue).invoke();
        } else {
            millis = Long.parseLong(sValue);
        }

        return millis;

    } catch (NumberFormatException e) {
        LOGGER.warn("Number format exception", e);
    }

    return 0;
}

सभी एक्सट्रैक्टएक्सएक्सएक्स तरीकों को staticआंतरिक वर्गों के रूप में परिभाषित किया गया है । उदाहरण के लिए, जैसे नीचे एक -

    private static class ExtractHour {
      private String sValue;

      public ExtractHour(String sValue) {
         this.sValue = sValue;
      }

      public long invoke() {
         long millis;
         millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 60 * 60 * 1000);
         return millis;
     }
 }

अद्यतन १

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

अमरूद Functionयहाँ एक अवांछित समारोह है। वर्तमान स्थिति के बारे में प्रश्न को अद्यतन करना चाहता था। यहां कुछ भी अंतिम नहीं है। कृपया अपने विचार डालें ..

public class DurationParse {

private static final Logger LOGGER = LoggerFactory.getLogger(DurationParse.class);
private static final Map<String, Function<String, Long>> MULTIPLIERS;
private static final Pattern STRING_REGEX = Pattern.compile("^(\\d+)\\s*(\\w+)");

static {

    MULTIPLIERS = new HashMap<>(7);

    MULTIPLIERS.put("S", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractSecond(input).invoke();
        }
    });

    MULTIPLIERS.put("s", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractInSecond(input).invoke();
        }
    });

    MULTIPLIERS.put("ms", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractMillisecond(input).invoke();
        }
    });

    MULTIPLIERS.put("m", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractInMinute(input).invoke();
        }
    });

    MULTIPLIERS.put("H", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractHour(input).invoke();
        }
    });

    MULTIPLIERS.put("d", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractDay(input).invoke();
        }
    });

    MULTIPLIERS.put("w", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractWeek(input).invoke();
        }
    });

}

public static long parseTimeValue(String sValue) {

    if (isNullOrEmpty(sValue)) {
        return 0;
    }

    Matcher matcher = STRING_REGEX.matcher(sValue.trim());

    if (!matcher.matches()) {
        LOGGER.warn(String.format("%s is invalid duration, assuming 0ms", sValue));
        return 0;
    }

    if (MULTIPLIERS.get(matcher.group(2)) == null) {
        LOGGER.warn(String.format("%s is invalid configuration, assuming 0ms", sValue));
        return 0;
    }

    return MULTIPLIERS.get(matcher.group(2)).apply(matcher.group(1));
}

private static class ExtractSecond {
    private String sValue;

    public ExtractSecond(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = Long.parseLong(sValue);
        return millis;
    }
}

private static class ExtractMillisecond {
    private String sValue;

    public ExtractMillisecond(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = (long) (Double.parseDouble(sValue));
        return millis;
    }
}

private static class ExtractInSecond {
    private String sValue;

    public ExtractInSecond(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = (long) (Double.parseDouble(sValue) * 1000);
        return millis;
    }
}

private static class ExtractInMinute {
    private String sValue;

    public ExtractInMinute(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = (long) (Double.parseDouble(sValue) * 60 * 1000);
        return millis;
    }
}

private static class ExtractHour {
    private String sValue;

    public ExtractHour(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = (long) (Double.parseDouble(sValue) * 60 * 60 * 1000);
        return millis;
    }
}

private static class ExtractDay {
    private String sValue;

    public ExtractDay(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = (long) (Double.parseDouble(sValue) * 24 * 60 * 60 * 1000);
        return millis;
    }
}

private static class ExtractWeek {
    private String sValue;

    public ExtractWeek(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = (long) (Double.parseDouble(sValue) * 7 * 24 * 60 * 60 * 1000);
        return millis;
    }
}

}


अद्यतन २

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


4
सबसे आसान OO जवाब ऑफहैंड: private static Dictionary<string,Func<string,long>> _mappingStringToParser;मैं आपके लिए एक अभ्यास के रूप में आराम को छोड़ दूंगा (या अभी की तुलना में अधिक खाली समय के साथ कोई व्यक्ति)। वहाँ एक क्लीनर एपीआई पाया जा सकता है अगर आप monadic parsers के साथ कोई परिचित है, लेकिन मैं अभी वहाँ नहीं जाऊँगा ..
जिमी Hoffa

अगर आप "मोनैडिक पार्सर्स" पर कुछ समय लगा सकते हैं और यह इस तरह के छोटे से छोटे फ़ंक्शन पर कैसे लागू किया जा सकता है, तो आपको अच्छा लगेगा। और, कोड का यह टुकड़ा जावा से है।
asyncwait

ExtractBlahकक्षाएं कैसे परिभाषित की जाती हैं? ये कुछ लाइब्रेरी या होमब्रे से हैं?
gnat

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

2
पुन: अद्यतन: क्या मैं केवल एक है जो मेरे सिर में "ओवर इंजीनियर" बज रहा है?
मटनज़

जवाबों:


46

सॉफ्टवेयर इंजीनियरिंग उत्तर:

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

बड़े बहुराष्ट्रीय निगम के लिए काम करने वाले मिनियन उत्तर:

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


14
नियम के कारण का सम्मान करने के लिए +1, नियम ही नहीं
राडु मर्जिया

5
खूब कहा है! हालाँकि, अन्य मामलों में जहां आपके पास CC = 13 कई घोंसले के स्तर और जटिल (ब्रांचिंग) तर्क है, कम से कम इसे सरल बनाने की कोशिश करना बेहतर होगा।
ग्रिजवाको

16

उनकी मदद के लिए @JimmyHoffa, @MichaelT, और @ GlenH7 को धन्यवाद!

अजगर

पहली चीजें पहले, आपको वास्तव में केवल ज्ञात उपसर्गों को स्वीकार करना चाहिए, जो कि 'एच' या 'एच' है। आप तो है दोनों स्वीकार करने के लिए, आप इसे अपने नक्शे में कमरे को बचाने के लिए सुसंगत बनाने के लिए कुछ कार्रवाई करने चाहिए।

अजगर में आप एक शब्दकोश बना सकते हैं।

EXTRACTION_MAP = {
    'S': ExtractSecond,
    'ms': ExtractMillisecond,
    'm': ExtractMinute,
    'H': ExtractHour,
    'd': ExtractDay,
    'w': ExtractWeek
}

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

def parseTimeValue(sValue)
    ending = ''.join([i for i in sValue if not i.isdigit()])
    return EXTRACTION_MAP[ending](sValue).invoke()

एक बेहतर चक्रीय जटिलता होनी चाहिए।


जावा

हमें प्रत्येक गुणक के केवल 1 (एक) की आवश्यकता है। कुछ अन्य उत्तरों के सुझाव के अनुसार उन्हें मानचित्र में रखें।

Map<String, Float> multipliers = new HashMap<String, Float>();
    map.put("S", 60 * 60);
    map.put("ms", 60 * 60 * 1000);
    map.put("m", 60);
    map.put("H", 1);
    map.put("d", 1.0 / 24);
    map.put("w", 1.0 / (24 * 7));

तब हम सही कनवर्टर को हथियाने के लिए मानचित्र का उपयोग कर सकते हैं

Pattern foo = Pattern.compile(".*(\\d+)\\s*(\\w+)");
Matcher bar = foo.matcher(sValue);
if(bar.matches()) {
    return (long) (Double.parseDouble(bar.group(1)) * multipliers.get(bar.group(2);
}

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

@FrustratedWithFormsDesigner वे हो सकते हैं, लेकिन उस पद्धति के दायरे में, इसकी बस एक लंबी और तात्कालिक वस्तु वापस आ रही है। एक तरफ के रूप में, इसका साइड इफेक्ट है अगर इस कोड को अधिक बार कहा जाता है, तो अक्सर उपयोग की जाने वाली संख्या, बिना किसी राज्य के साथ रहने वाली छोटी वस्तुओं को काट दिया जाता है।

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

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

5
@mattnz - 1) ओपी उनके उदाहरण में कभी निर्दिष्ट नहीं करता है और परवाह नहीं कर सकता है। आप कैसे जानते हैं कि मान्यताएँ अमान्य हैं? 2) सामान्य विधि अभी भी काम करेगी, संभवतः एक अधिक जटिल regex की आवश्यकता होगी। 3) उत्तर का बिंदु साइक्लोमैटिक जटिलता को हल करने के लिए एक वैचारिक मार्ग प्रदान करना है, और जरूरी नहीं कि यह एक विशिष्ट, संकलन योग्य उत्तर हो। 4) जबकि यह उत्तर "जटिलता की बात करता है" के व्यापक पहलू को अनदेखा करता है, यह कोड के लिए एक वैकल्पिक रूप दिखा कर इस मुद्दे का अप्रत्यक्ष रूप से जवाब देता है।

3

चूँकि आप return millisउस भयानक ifelseifelse के अंत में वैसे भी, पहली बात जो दिमाग में आती है, वह है मानों को तुरंत if- block के भीतर से वापस करना। यह दृष्टिकोण गार्ड क्लॉज के साथ रिप्लेस्ड नेस्ट कंडिशनल के रूप में रिफैक्टिंग पैटर्न की सूची में सूचीबद्ध है ।

एक विधि में सशर्त व्यवहार होता है जो स्पष्ट नहीं करता है कि निष्पादन का सामान्य मार्ग क्या है

सभी विशेष मामलों के लिए गार्ड क्लॉज का उपयोग करें

यह आपको दूसरे से छुटकारा पाने में मदद करेगा, कोड को समतल करेगा और सोनार को खुश करेगा:

    if (sValue.endsWith("S")) {
        return new ExtractSecond(sValue).invoke();
    } // no need in else after return, code flattened

    if (sValue.endsWith("ms")) {
        return new ExtractMillisecond(sValue).invoke();
    }

    // and so on...
    return Long.parseLong(sValue); // forget millis, these aren't needed anymore

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

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


एक उदाहरण के लिए आपका कोड स्निपेट ExtractHour मुझे लगता है कि ExtractXXX कार्यक्षमता को एक प्रकार से इष्टतम से डिज़ाइन किया गया है। मैं शेष कक्षाओं के हर शर्त बिना सोचे-समझे दोहराता ही parseDoubleऔर substring, और 60 और 1000 और बार बार से अधिक की तरह सामान गुणा।

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

private static class ParseHelper {
    // three things you need to know to parse:
    final String source;
    final int charsToCutAtEnd;
    final long multiplier;

    ParseHelper(String source, int charsToCutAtEnd, long multiplier) {
        this.source = source == null ? "0" : source; // let's handle null here
        this.charsToCutAtEnd = charsToCutAtEnd;
        this.multiplier = multiplier;
    }

    long invoke() {
        // NOTE consider Long.parseLong instead of Double.parseDouble here
        return (long) (Double.parseDouble(cutAtEnd()) * multiplier);
    }

    private String cutAtEnd() {
        if (charsToCutAtEnd == 0) {
            return source;
        }
        // write code that cuts 'charsToCutAtEnd' from the end of the 'source'
        throw new UnsupportedOperationException();
    }
}

उसके बाद, आपको एक कोड की आवश्यकता होगी जो किसी विशेष स्थिति में वस्तुओं के ऊपर कॉन्फ़िगर करता है अगर यह पूरा होता है, या किसी भी तरह "बाईपास" अन्यथा। यह इस प्रकार किया जा सकता है:

private ParseHelper setupIfInSecond(ParseHelper original) {
    final String sValue = original.source;
    return sValue.endsWith("s") && !sValue.endsWith("ms")
            ? new ParseHelper(sValue, 1, 1000)
            :  original; // bypass
}

private ParseHelper setupIfMillisecond(ParseHelper original) {
    final String sValue = original.source;
    return sValue.endsWith("ms")
            ? new ParseHelper(sValue, 2, 1)
            : original; // bypass
}

// and so on...

उपरोक्त बिल्डिंग ब्लॉक्स के आधार पर , आपकी विधि का कोड निम्नानुसार देखा जा सकता है:

public long parseTimeValue(String sValue) {

   return setupIfSecond(
           setupIfMillisecond(
           setupIfInSecond(
           setupIfInMinute(
           setupIfHour(
           setupIfDay(
           setupIfWeek(
           new ParseHelper(sValue, 0, 1))))))))
           .invoke();
}

आप देखें, कोई जटिलता नहीं बची है, विधि के अंदर कोई घुंघराले ब्रेसिज़ नहीं हैं (और न ही कई रिटर्न जैसे मेरे मूल ब्रूट बल सुझाव चपटे कोड पर)। आप बस क्रमिक रूप से इनपुट की जांच करते हैं और आवश्यकतानुसार प्रसंस्करण को समायोजित करते हैं।


1

यदि आप वास्तव में इसे रिफैक्ट करना चाहते हैं, तो आप कुछ इस तरह से कर सकते हैं:

// All of your Extract... classes will have to implement this interface!
public Interface TimeExtractor
{
    public long invoke();
}

private static class ExtractHour implements TimeExtractor
{
  private String sValue;


  /*Not sure what this was for - might not be necessary now
  public ExtractHour(String sValue)
  {
     this.sValue = sValue;
  }*/

  public long invoke(String s)
  {
     this.sValue = s;
     long millis;
     millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 60 * 60 * 1000);
     return millis;
 }
}

private static HashMap<String, TimeExtractor> extractorMap= new HashMap<String, TimeExtractor>();

private void someInitMethod()
{
   ExtractHour eh = new ExtractorHour;
   extractorMap.add("H",eh);
   /*repeat for all extractors */
}

public static long parseTimeValue(String sValue)
{
    if (sValue == null)
    {
        return 0;
    }
    String key = extractKeyFromSValue(sValue);
    long millis;
    TimeExtractor extractor = extractorMap.get(key);
    if (extractor!=null)
    {
      try
      {
         millis= extractor.invoke(sValue);
      }
        catch (NumberFormatException e)
      {
          LOGGER.warn("Number format exception", e);
      }
    }
    else
       LOGGER.error("NO EXTRACTOR FOUND FOR "+key+", with sValue: "+sValue);

    return millis;
}

विचार यह है कि आपके पास कुंजी का एक मानचित्र है (जो हर समय "एंडवाइस" में आपके उपयोग से है) विशिष्ट वस्तुओं के लिए मानचित्र जो आप चाहते हैं कि प्रसंस्करण करते हैं।

यह यहाँ थोड़ा कठिन है, लेकिन मुझे आशा है कि यह पर्याप्त स्पष्ट है। मैं इस बात का विवरण नहीं भर पाया extractKeyFromSValue()क्योंकि मुझे यह पता नहीं है कि ये तार ठीक से क्या कर रहे हैं। ऐसा लगता है कि यह अंतिम 1 या 2 गैर-संख्यात्मक वर्ण है (एक रेगीक्स शायद इसे आसानी से पर्याप्त रूप से निकाल सकता है, शायद.*([a-zA-Z]{1,2})$ काम करेगा), लेकिन मुझे 100% यकीन नहीं है ...


मूल उत्तर:

आप बदल सकते हैं

else if (sValue.endsWith("H") || sValue.endsWith("h")) {

सेवा मेरे

else if (sValue.toUpper().endsWith("H")) {

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

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


3
ज्यादा इस्तेमाल नहीं, सोनार अभी भी शिकायत करता है। मैं इसे मनोरंजन के लिए करने की कोशिश कर रहा हूं, कम से कम एक या दो सीखने की अनुमति देता है। कोड को मचान पर स्थानांतरित कर दिया गया है।
asyncwait

@asyncwait: आह, मुझे लगा कि आप सोनार से इस रिपोर्ट को अधिक गंभीरता से ले रहे हैं। हाँ, मेरे द्वारा सुझाए गए परिवर्तन से बहुत फर्क नहीं पड़ेगा - मुझे नहीं लगता कि यह आपको 13 से 10 तक ले जाएगा, लेकिन कुछ उपकरणों में मैंने देखा है कि इस तरह की चीज कुछ हद तक ध्यान देने योग्य है।
FrustratedWithFormsDesigner

बस एक और IF शाखा जोड़ने पर CC को +1 तक बढ़ाया गया।
asyncwait

0

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

//utility class for matching values
private static class ValueMatchingPredicate implements Predicate<String>{
    private final String[] suffixes;

    public ValueMatchingPredicate(String[] suffixes) {      
        this.suffixes = suffixes;
    }

    public boolean apply(String sValue) {
        if(sValue == null) return false;

        for (String suffix : suffixes) {
            if(sValue.endsWith(suffix)) return true;
        }

        return false;
    }

    public static Predicate<String> withSuffix(String... suffixes){         
        return new ValueMatchingPredicate(suffixes);
    }       
}

//enum containing all possible options
private static enum TimeValueExtractor {                
    SECOND(
        ValueMatchingPredicate.withSuffix("S"), 
        new Function<String, Long>(){ 
            public Long apply(String sValue) {  return new ExtractSecond(sValue).invoke(); }
        }),

    MILISECOND(
        ValueMatchingPredicate.withSuffix("ms"), 
        new Function<String, Long>(){
            public Long apply(String sValue) { return new ExtractMillisecond(sValue).invoke(); }
        }),

    IN_SECOND(
        ValueMatchingPredicate.withSuffix("s"),
        new Function<String, Long>(){
            public Long apply(String sValue) { return new ExtractInSecond(sValue).invoke(); }
        }),

    IN_MINUTE(
        ValueMatchingPredicate.withSuffix("m"),
        new Function<String, Long>(){
            public Long apply(String sValue) {  return new ExtractInMinute(sValue).invoke(); }
        }),

    HOUR(
        ValueMatchingPredicate.withSuffix("H", "h"),
        new Function<String, Long>(){
            public Long apply(String sValue) {  return new ExtractHour(sValue).invoke(); }
        }),

    DAY(
        ValueMatchingPredicate.withSuffix("d"),
        new Function<String, Long>(){
            public Long apply(String sValue) {  return new ExtractDay(sValue).invoke(); }
        }),

    WEEK(
        ValueMatchingPredicate.withSuffix("w"),
        new Function<String, Long>(){
            public Long apply(String sValue) {  return new ExtractWeek(sValue).invoke(); }
        });

    private final Predicate<String>      valueMatchingRule;
    private final Function<String, Long> extractorFunction;

    public static Long DEFAULT_VALUE = 0L;

    private TimeValueExtractor(Predicate<String> valueMatchingRule, Function<String, Long> extractorFunction) {
        this.valueMatchingRule = valueMatchingRule;
        this.extractorFunction = extractorFunction;
    }

    public boolean matchesValueSuffix(String sValue){
        return this.valueMatchingRule.apply(sValue);
    }

    public Long extractTimeValue(String sValue){
        return this.extractorFunction.apply(sValue);
    }

    public static Long extract(String sValue) throws NumberFormatException{
        TimeValueExtractor[] extractors = TimeValueExtractor.values();

        for (TimeValueExtractor timeValueExtractor : extractors) {
            if(timeValueExtractor.matchesValueSuffix(sValue)){
                return timeValueExtractor.extractTimeValue(sValue);
            }
        }

        return DEFAULT_VALUE;
    }
}

//your function
public static long parseTimeValue(String sValue){
    try{
        return TimeValueExtractor.extract(sValue);
    } catch (NumberFormatException e) {
        //LOGGER.warn("Number format exception", e);
        return TimeValueExtractor.DEFAULT_VALUE;
    }
}

0

आपकी टिप्पणी से संबंधित:

निचला रेखा - सोनार (या हाफ बेक्ड प्रोजेक्ट मैनेजर) की खातिर इंजीनियर से सीसी के बारे में शिकायत नहीं करते। बस क्या परियोजना के लिए एक पैसा लायक है।

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

लेकिन उन परिस्थितियों के जवाब में टीम के मानकों को बदलना जो समझ में नहीं आते हैं, मानकों के बारे में सही दृष्टिकोण के साथ एक अच्छी टीम का संकेत है। टीम की मदद करने के लिए मानक मौजूद हैं, कोड लिखने के तरीके में नहीं।


0

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

public static long parseTimeValue(String sValue) {

    if (sValue == null) {
        return 0;
    }

    try {
        return getMillis(sValue);
    } catch (NumberFormatException e) {
        LOGGER.warn("Number format exception", e);
    }

    return 0;
}

private static long getMillis(String sValue) {
    if (sValue.endsWith("S")) {
        return new ExtractSecond(sValue).invoke();
    } else if (sValue.endsWith("ms")) {
        return new ExtractMillisecond(sValue).invoke();
    } else if (sValue.endsWith("s")) {
        return new ExtractInSecond(sValue).invoke();
    } else if (sValue.endsWith("m")) {
        return new ExtractInMinute(sValue).invoke();
    } else if (sValue.endsWith("H") || sValue.endsWith("h")) {
        return new ExtractHour(sValue).invoke();
    } else if (sValue.endsWith("d")) {
        return new ExtractDay(sValue).invoke();
    } else if (sValue.endsWith("w")) {
        return new ExtractWeek(sValue).invoke();
    } else {
        return Long.parseLong(sValue);
    }
}

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

इसके अलावा, क्लीन कोड के पाठक इस तथ्य का आनंद ले सकते हैं कि शीर्ष स्तर की विधि अब सरल और संक्षिप्त है, जबकि निकाले गए एक विवरण के साथ संबंधित है।

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