複雑すぎる方法を避ける-循環的な複雑さ


23

Cyclomatic Complexityを削減するためにこの方法をどのように実行するかわからない。ソナーは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の回答を受け入れています。それが道であり、この質問にぶつかった人に悪い例を設定したくないからです。結論-Sonar(またはHalf Baked Project Manager)のためにCCについて文句を言うのはやめましょう。プロジェクトに1ペニーの価値があることを実行してください。ありがとうございます。


4
最も簡単なオブジェクト指向の回答:private static Dictionary<string,Func<string,long>> _mappingStringToParser;残りは演習(または、現在私よりも空き時間がある人)の演習として残しておきます。...そこにはモナドのパーサーを持つ任意の知識があれば発見されるクリーンなAPIがありますが、私は今そこに行くことはありません
ジミー・ホッファ

「モナドパーサー」にいつか余裕を持たせて、このような非常に小さな関数にそれを適用する方法をお探しですか。そして、このコードはJavaのものです。
asyncwait

ExtractBlahクラスはどのように定義されていますか?これらはいくつかの図書館や自作のものですか?
gnat

4
追加されたコードを使用すると、実装がより単純になります。実際の分散は乗算器です。これらのマップを作成します。sValueの末尾からアルファ文字を引き出し、それらをキーとして使用し、マップされた乗算器で乗算する値の前面からアルファまですべて引き出します。
ジミー・ホッファ

2
再更新:私の頭の中で「過剰な設計」が鳴っているのは私だけですか?
マッテンツ

回答:


46

ソフトウェアエンジニアリングの回答:

これは、数えるのが簡単なBeanを数えるだけで間違ったことをする多くのケースの1つにすぎません。複雑な機能ではありません。変更しないでください。Cyclomatic Complexityは、単に複雑さのガイドに過ぎず、それに基づいてこの関数を変更する場合、複雑さをあまり使用していません。シンプルで、読みやすく、保守可能です(今のところ)、将来的に大きくなると、CCは指数関数的に急上昇し、以前ではなく、必要なときに必要な注意を引きます。

大規模な多国籍企業で働くミニオン回答:

組織には、Beanカウンターの過剰な非生産的なチームがいっぱいです。Beanカウンターを幸せに保つことは、正しいことをするよりも簡単で、確かに賢明です。彼のCCを10に下げるためにルーチンを変更する必要がありますが、なぜそれをしているのかについて正直に言ってください。コメントで示唆されているように、「単項パーサー」が役立つ場合があります


14
+1ルール自体ではなく、ルールの理由を尊重するために
ラドゥムゼア

5
よく言った!ただし、いくつかのネストレベルと複雑な(分岐)ロジックを含むCC = 13がある他のケースでは、少なくとも単純化することをお勧めします。
グリズワコ

16

@ JimmyHoffa、@ MichaelT、および@ GlenH7の支援に感謝します!

Python

まず最初に、既知のプレフィックス、つまり '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-提供されている例の中の変数名をもう一度見てください。OPが文字列として時間単位を受け取っており、それを別のタイプの時間単位に変換する必要があることは明らかです。したがって、この回答で提供されている例は、OPのドメインに直接関係しています。その側面を無視して、答えはまだ別のドメインに使用できる一般的なアプローチを提供します。この答えがない問題が提示された問題解決提示されているが。

5
@mattnz-1)OPはその例では決して指定しないので、気にしないかもしれません。仮定が無効であることをどうやって知るのですか?2)一般的な方法は引き続き機能し、より複雑な正規表現が必要になる可能性があります。3)答えのポイントは、循環的な複雑さを解決するための概念的なルートを提供することであり、必ずしも特定のコンパイル可能な答えではありません。4)この回答では、「複雑さは問題になりますか?」という広範な側面を無視していますが、コードの代替形式を示すことで間接的に問題に回答しています。

3

とにかくreturn millisひどいifelseifelseの最後にいるので、まず頭に浮かぶのは、ifブロック内からすぐに値を返すことです。このアプローチは、リファクタリングパターンのカタログに「入れ子になった条件付き置換ガードガード」としてリストされているアプローチに従います。

メソッドには、実行の通常のパスが何であるかを明確にしない条件付き動作があります

すべての特別な場合にガード条項を使用する

それはあなたが他のものを取り除き、コードを平らにし、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

検討する価値があるもう1つのことは、try-catchブロックを削除することです。これにより循環的複雑度も低下しますが、このブロックを使用することをお勧めする主な理由は、呼び出し元コードが法的に解析された0と数値形式の例外を区別する方法がないことです。

解析エラーに対して0を返すことが呼び出し側コードに必要なものであることが200%確実でない限り、その例外を上に伝播させ、呼び出し側コードに対処方法を決定させることができます。通常、実行を中止するか、入力の取得を再試行するか、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;
}

アイデアは、必要な処理を行う特定のオブジェクトにマップするキーのマップ(「endsWith」で常に使用するもの)があることです。

ここでは少し荒いですが、十分に明確であることを願っています。私はextractKeyFromSValue()これらの文字列が適切にそれを行うために十分なことを知らないので、詳細を記入しませんでした。最後の1つまたは2つの数字以外の文字のように見えます(正規表現はおそらく十分に簡単に抽出でき、おそらく機能するでしょ.*([a-zA-Z]{1,2})$う)が、私は100%確信していません...


元の回答:

あなたは変えることができます

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

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

それはあなたを少し救うかもしれませんが、正直なところ、私はそれについてあまり心配しません。メソッドをそのままにしておくことに大きな害はないと思います。「Sonarのルールに従う」ことを試みる代わりに、「合理的に可能な限りSonarのガイドラインに近づけるようにする」ことを試みてください。

これらの分析ツールに含まれるすべてのルールに従うことを試みて気が狂う可能性がありますが、ルールがプロジェクトにとって意味があるかどうか、リファクタリングに費やした時間が価値がないかもしれない特定のケースについても決定する必要があります。


3
あまり役に立たない、ソナーはまだ文句を言います。少なくとも1つか2つを学ぶことができます。ただし、コードはステージングに移動します。
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ペニーの価値があることを実行してください。

考慮すべきもう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.