Уникайте занадто складного методу - цикломатичної складності


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;
}

Всі методи ExtractXXX визначаються як 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;
     }
 }

ОНОВЛЕННЯ 1

Я збираюся влаштуватись із сукупністю пропозицій, щоб задовольнити хлопця Сонара. Однозначно можливість вдосконалення та спрощення.

Гуава 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;
    }
}

}


ОНОВЛЕННЯ 2

Хоча я додав своє оновлення, це лише те, що варто того часу. Я буду рухатись далі, оскільки Сонар зараз не скаржиться. Не хвилюйтесь багато, і я приймаю відповідь mattnz, оскільки це шлях, і я не хочу наводити поганий приклад для тих, хто стикається з цим питанням. Підсумок - Не надто інженер заради Сонара (або напівзапеченого керівника проекту) скаржиться на CC. Просто зробіть те, що коштує копійки за проект. Дякую всім.


4
Найпростіша відповідь OO без зволікань: private static Dictionary<string,Func<string,long>> _mappingStringToParser;решту я залишу для вас (або для когось більше вільного часу, ніж я). Є чистіший API, якщо ви знайомі з монадійними аналізаторами, але я зараз не піду туди ..
Jimmy Hoffa

Буде радий, якби ви могли пощастити колись на "монадійних аналізаторах" і як це можна застосувати до досить маленької функції, як ця. І цей фрагмент коду від Java.
asyncwait

як визначаються ExtractBlahкласи? це з якоїсь бібліотеки чи домашньої мови?
гнат

4
Доданий код веде мене трохи далі до більш простої реалізації: Ваша фактична дисперсія - це множник. Створіть карту з них: Витягніть альфа-символи з кінця свого sValue, використовуйте їх як свій ключ, а потім витягніть всі, поки альфа не переходить на передню частину для значення, яке ви помножуєте на зіставленому множнику.
Джиммі Хоффа

2
Поновити оновлення: я єдиний, у кого в голові дзвонить "Над інженером"?
mattnz

Відповіді:


46

Відповідь програмного забезпечення:

Це лише один із численних випадків, коли просто підрахунок бобів, який легко порахувати, змусить вас зробити неправильну справу. Це не є складною функцією, не змінюйте її. Цикломатична складність - це лише посібник щодо складності, і ви погано використовуєте її, якщо змінити цю функцію на її основі. Його простий, читабельний, його реконструйований (поки що), якщо в майбутньому він стає більшим, КК зросте в експоненціальному масштабі, і він отримає увагу, яке йому потрібно, коли йому це потрібно, а не раніше.

Міньйон, який працює над великою транснаціональною корпорацією Відповідь:

Організації наповнені переплаченими, непродуктивними командами лічильників бобів. Задовольнити лічильники квасолі простіше і простіше, але мудріше, ніж робити правильно. Вам потрібно змінити розпорядок, щоб він знизив КС до 10, але будьте чесними, чому ви це робите, - щоб утримати лічильник бобів від спини. Як запропоновано в коментарях - "монадійні аналізатори" можуть допомогти


14
+1 за повагу причини правила, а не самого правила
Раду Мурзеа

5
Добре сказано! Однак в інших випадках, коли у вас є CC = 13 з декількома рівнями гніздування та складною (розгалуженою) логікою, бажано хоча б спробувати спростити її.
grizwako

16

Дякуємо @JimmyHoffa, @MichaelT та @ GlenH7 за допомогу!

Пітон

По-перше, спочатку ви повинні приймати лише відомі префікси, тобто "H" або "h". Якщо ви повинні прийняти обидва, вам слід виконати певну операцію, щоб зробити так, щоб зберегти місце на карті.

У python ви можете створити словник.

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()

Повинно мати кращу цикломатичну складність.


Java

Нам потрібно лише 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 вони можуть, але в рамках цього методу його просто повернення довгий і екземплярний об'єкт випадає із сфери застосування. На відміну від цього, це має побічний ефект, якщо цей код називається частіше, кількість часто використовуваних короткоживучих об'єктів без стану зменшується.

Жоден з цих відповідей не може вважатися таким же рішенням, що і оригінальна програма, оскільки вони покладаються на припущення, які можуть бути неправдивими. Код Java: Як ви впевнені, що єдине, що роблять методи, це застосувати множник? Код Python: як ви впевнені, що рядок не може містити провідні (або середні точки) символи, окрім цифр (напр., "123.456s").
mattnz

@mattnz - по-іншому подивіться на назви змінних у наданих прикладах. Зрозуміло, що ОП отримує одиницю часу як рядок, а потім потрібно перетворити її в інший тип одиниці часу. Тож приклади, надані у цій відповіді, стосуються безпосередньо сфери роботи ОП. Ігноруючи цей аспект, відповідь все ж надає загальний підхід, який може бути використаний для іншого домену. Ця відповідь вирішує проблему, яка була представлена, а не проблемою, яка могла бути представлена.

5
@mattnz - 1) ОП ніколи не вказує, що у їхньому прикладі і може не хвилювати. Як ви знаєте, що припущення недійсні? 2) Загальний метод все-таки буде працювати, потенційно вимагаючи більш складного регулярного вираження. 3) Суть відповіді полягає у наданні концептуального шляху до вирішення циклічної складності, а не обов'язково конкретної, складової відповіді. 4) хоча ця відповідь ігнорує більш широкий аспект "чи має значення складність", він опосередковано відповідає на питання, показуючи альтернативну форму для коду.

3

Оскільки ви return millisв кінці цього жахливого ifelseifelse все одно, перше, що вам спадає на думку, - це повернути значення негайно зсередини if-блоків. Цей підхід слід за тим, який вказаний у каталозі моделей рефакторингу як Замінити вкладені умови умовними положеннями охорони .

Метод має умовну поведінку, яка не дає зрозуміти, який нормальний шлях виконання

Використовуйте положення про охорону для всіх особливих випадків

Це допоможе вам позбутися чужих, згладити код і зробити Сонар щасливим:

    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;
}

Ідея полягає в тому, що у вас є карта ключів (що ви використовуєте в "endWith" весь час), які відображають конкретні об'єкти, які виконують обробку, яку ви хочете.

Тут трохи грубо, але я сподіваюся, що це досить зрозуміло. Я не заповнив деталі, 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

Ви можете розглянути можливість використання enum для зберігання всіх наявних справ та предикатів для відповідності значень. Як зазначалося раніше, ваша функція читається досить, щоб залишити її незмінною. Ці показники є, щоб допомогти вам не навпаки.

//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

Пов’язаний із вашим коментарем:

Підсумок - Не надто інженер заради Сонара (або напівзапеченого керівника проекту) скаржиться на CC. Просто зробіть те, що коштує копійки за проект.

Ще один варіант, який слід врахувати, - це зміни стандартів кодування вашої команди для таких ситуацій. Можливо, ви можете додати до якогось командного голосування, щоб забезпечити міру управління та уникнути коротких ситуацій.

Але зміна стандартів команди у відповідь на ситуації, які не мають сенсу, є ознакою хорошої команди з правильним ставленням до стандартів. Стандарти існують, щоб допомогти команді, а не заважати писати код.


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 та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.