너무 복잡한 방법을 피하십시오-Cyclomatic Complexity


23

Cyclomatic Complexity를 줄이기 위해이 방법을 사용하는 방법을 모르겠습니다. Sonar는 13을보고하고 10은 예상됩니다. 나는이 방법을 그대로 두는 데 아무런 해가 없다고 확신하지만 Sonar의 규칙을 준수하는 방법에 대해 나에게 도전하고 있습니다. 모든 생각은 크게 감사하겠습니다.

 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

나는 Sonar 녀석을 만족시키기 위해 여기에 여러 제안을 정리할 것입니다. 개선과 단순화를위한 확실한 공간.

구아바 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

업데이트를 추가했지만 그만한 가치가 있습니다. Sonar가 불평하지 않기 때문에 앞으로 나아갈 것입니다. 걱정하지 말고 mattnz 답변을 받아들입니다.이 질문에 부딪 치는 사람들에게 나쁜 모범을 보이고 싶지 않습니다. 결론-Sonar (또는 Half Baked Project Manager)를 위해 오버 엔지니어링하지 마십시오 CC에 대해 불평합니다. 프로젝트를 위해 1 페니의 가치가있는 것을하십시오. 모두 감사합니다.


4
가장 쉬운 OO 답변 : private static Dictionary<string,Func<string,long>> _mappingStringToParser;나는 나머지를 당신을위한 운동으로 남겨 두겠습니다 (또는 지금보다 여가 시간이 더 많은 사람). 모나 딕 파서에 익숙하지만 지금은 가지 않을 것입니다.
Jimmy Hoffa

"모나 딕 파서"에 언젠가 여유가 있고이 기능과 같은 아주 작은 기능에 어떻게 적용 할 수 있다면 좋을까요? 그리고이 코드는 Java에서 온 것입니다.
asyncwait

ExtractBlah클래스 는 어떻게 정의됩니까? 이것들은 어떤 도서관이나 사제에서 왔습니까?
gnat

4
추가 된 코드는 간단한 구현으로 조금 더 나아갑니다. 실제 분산은 멀티 플라이어입니다. 다음의 맵을 작성하십시오. sValue의 끝에서 알파 문자를 당기고이를 키로 사용한 다음 앞의 알파가 맵핑 된 멀티 플라이어에 의해 다중화 된 값에 대해 모두 올 때까지 모두 당기십시오.
지미 호파

2
다시 업데이트 : 나는 "Over Engineered"라는 사람이 내 머리에 울리는 유일한 사람입니까?
mattnz

답변:


46

소프트웨어 엔지니어링 답변 :

이것은 계산하기 쉬운 콩을 단순히 계산하면 잘못된 일을하게하는 많은 경우 중 하나입니다. 복잡한 기능이 아니므로 변경하지 마십시오. Cyclomatic Complexity는 복잡성에 대한 지침 일 뿐이며이 기능을 기반으로이 기능을 변경하면 잘못 사용하고 있습니다. 간단하고 읽기 쉽고 유지 관리가 가능합니다 (현재). 앞으로 더 커지면 CC는 기하 급수적으로 급등하고 필요할 때 필요할 때주의를 기울일 것입니다.

대규모 다국적 기업을위한 Minion 답변 :

조직은 과잉, 비생산적인 콩 카운터 팀으로 가득합니다. 콩 카운터를 행복하게 유지하는 것은 올바른 일을하는 것보다 쉽고 쉽고 현명합니다. CC를 10으로 낮추려면 루틴을 변경해야하지만, 왜 그렇게하고 있는지에 대해 정직해야합니다. – 빈 카운터를 등으로부터 보호하십시오. 의견에서 제안한 바와 같이 "모나 딕 파서"가 도움이 될 수 있습니다.


14
규칙 자체가 아니라 규칙의 이유를 존중하여 +1
Radu Murzea

5
잘했다! 그러나 여러 중첩 수준과 복잡한 (분기) 논리를 가진 CC = 13 인 다른 경우에는 최소한 단순화하려고 시도하는 것이 좋습니다.
grizwako

16

도움을 주신 @JimmyHoffa, @MichaelT 및 @ GlenH7에게 감사합니다!

파이썬

우선 먼저 알려진 접두사, 즉 'H'또는 'h'만 받아 들여야합니다. 당신 있다면둘 다 수락 맵에서 공간을 절약하기 위해 일부 작업을 수행해야합니다.

파이썬에서는 사전을 만들 수 있습니다.

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 그것들은 그 메소드의 범위에서 길고 리턴 된 인스턴스 객체가 범위를 벗어납니다. 이 코드를 더 자주 호출하면 상태가없는 자주 사용하는 수명이 짧은 객체의 수가 줄어드는 부작용이 있습니다.

이 답변들 중 어느 것도 유효하지 않을 수있는 가정에 의존하기 때문에 Original 프로그램과 동일한 솔루션을 제공하는 것으로 간주 될 수 없습니다. Java 코드 : 메소드가 승수를 적용하는 것만 어떻게 확신합니까? 파이썬 코드 : 문자열이 숫자 이외의 선행 (또는 중간 지점) 문자를 포함 할 수 없다는 것을 어떻게 확신합니까 (예 : "123.456s").
mattnz

@mattnz-제공된 예제 내에서 변수 이름을 다시 살펴보십시오. OP가 시간 단위를 문자열로 수신 한 다음 다른 유형의 시간 단위로 변환해야합니다. 따라서이 답변에 제공된 예제는 OP의 도메인과 직접 관련이 있습니다. 이러한 측면을 무시하면서도 대답은 여전히 ​​다른 도메인에 사용될 수있는 일반적인 접근법을 제공합니다. 이 답변하지 않는 문제를 제시 한 문제 해결 수 있습니다 제시되었다합니다.

5
@mattnz-1) OP는 예제에서이를 지정하지 않으며 신경 쓰지 않을 수 있습니다. 가정이 유효하지 않다는 것을 어떻게 알 수 있습니까? 2) 일반적인 방법은 여전히 ​​작동하며 더 복잡한 정규 표현식이 필요할 수 있습니다. 3) 답의 핵심은 순환 복잡성을 해결하기위한 개념적 경로를 제공하는 것이며, 반드시 구체적이고 컴파일 가능한 답은 아닙니다. 4)이 답변은 "복잡성 문제는 무엇입니까?"의 광범위한 측면을 무시하지만 코드의 대체 형식을 보여줌으로써 문제에 간접적으로 답변합니다.

3

어쨌든 return millis그 끔찍한 ifelseifelse 의 끝에서 당신이 생각하는 첫 번째 것은 if-blocks에서 즉시 값을 반환하는 것입니다. 이 접근 방식은 리팩토링 패턴 카탈로그에 나열된 중첩 조건부를 가드 절로 바꾸기 로 나열된 방법을 따릅니다 .

메소드에는 정상적인 실행 경로가 무엇인지 명확하지 않은 조건부 동작이 있습니다.

모든 특별한 경우에 가드 조항 사용

다른 코드를 없애고 코드를 평평하게 만들고 Sonar를 행복하게 만드는 데 도움이됩니다.

    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

고려해야 할 또 다른 사항은 try-catch 블록을 삭제하는 것입니다. 이것은 또한 순환 복잡성을 낮추지 만, 내가 추천하는 주된 이유는이 블록을 사용하면 호출자 코드가 법적으로 구문 분석 된 0을 숫자 형식 예외와 구별 할 수있는 방법이 없기 때문입니다.

구문 분석 오류에 대해 0을 반환하는 것이 호출자 코드에 필요한 것이라고 200 % 확신하지 않는 한, 예외를 더 잘 전파하고 호출자 코드가 처리 방법을 결정하도록하십시오. 일반적으로 호출자에게 실행을 중단할지 또는 입력을 다시 시도할지 또는 0 또는 -1과 같은 기본값으로 돌아가는지 결정하는 것이 더 편리합니다.


ExtractHour 예제의 코드 스 니펫을 사용하면 ExtractXXX 기능이 최적의 방식으로 설계되지 않았다고 생각합니다. 나는 남은 모든 클래스가 무의식적으로 똑같이 반복parseDouble 되고 substring60과 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;
}

아이디어는 원하는 처리를 수행하는 특정 객체에 매핑되는 키 맵 ( "endsWith"에서 항상 사용하는 것)을 가지고 있다는 것입니다.

여기는 약간 거칠지 만 충분히 명확하기를 바랍니다. 나는 extractKeyFromSValue()이 문자열이 제대로 무엇을하는지 충분히 알지 못하기 때문에 세부 사항을 채우지 않았습니다. 마지막 1 또는 2가 아닌 숫자가 아닌 것처럼 보입니다 (정규식은 아마 쉽게 추출 할 수 있습니다. 어쩌면 .*([a-zA-Z]{1,2})$작동 할 것입니다).


원래 답변 :

당신은 변경할 수 있습니다

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

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

그것은 당신을 조금 절약 할 수 있지만, 솔직히 말해서, 나는 그것에 대해 너무 걱정하지 않을 것입니다. 나는 그 방법을 그대로 두는 데 큰 해를 끼치 지 않는다고 생각합니다. "Sonar의 규칙을 준수"하려고 시도하는 대신 "합리적으로 가능한 한 Sonar의 지침을 준수하십시오".

이러한 분석 도구에 포함 된 모든 단일 규칙을 따르려고 노력할 경우 자신을 화나게 할 수 있지만, 규칙이 프로젝트에 적합한 지 여부와 리팩토링에 소요 된 시간이 가치가 없을 수있는 특정 사례에 대해서도 결정해야합니다. .


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

귀하의 의견과 관련 :

결론-Sonar (또는 Half Baked Project Manager)를 위해 오버 엔지니어링하지 마십시오 CC에 대해 불평합니다. 프로젝트를 위해 1 페니의 가치가있는 것을하십시오.

고려해야 할 또 다른 옵션은 이와 같은 상황에서 팀의 코딩 표준을 변경하는 것입니다. 아마도 일종의 팀 투표를 추가하여 거버넌스를 측정하고 바로 가기 상황을 피할 수 있습니다.

그러나 이해가되지 않는 상황에 대응하여 팀의 표준을 변경하는 것은 표준에 대한 올바른 태도를 가진 좋은 팀의 신호입니다. 표준은 코드 작성에 방해가되지 않고 팀을 돕기 위해 존재합니다.


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이어야하며 여전히 요구 사항을 통과합니다. 그리고 기본적 으로 이전과 같은 코드입니다 입니다. 코드는 처음부터 좋았 기 때문에 좋은 것입니다.

또한 Clean Code 독자는 최상위 방법이 간단하고 짧으며 추출 된 방법이 세부 사항을 처리한다는 사실을 즐길 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.