反復法は循環的複雑さを軽減し、サポート性を向上させますか?


11

C#、JavaScript、および(できれば)Java 8などの現代言語で一般的に見られるような反復メソッドは、コードの理解可能性およびサポート可能性に対する循環的複雑さの影響を軽減しますか?

たとえば、C#では次のコードを使用できます。

List<String> filteredList = new List<String>();

foreach (String s in originalList){
   if (matches(s)){
      filteredList.add(s);
   }
}

これには、単純な循環的複雑度2があります。

これを次のように簡単に書き換えることができます。

List<String> filteredList = originalList.where(s => matches(s));

単純な循環的複雑度は0です。

これにより、実際にサポート可能なコードが増えますか?このトピックに関する実際の研究はありますか?


2
タイトルは循環的複雑度の削減について質問していますが、テキストはCCの削減を想定しており、保守性について質問しています。これは貴重な質問になる可能性がありますが、タイトルをより正確に編集できますか?
キリアンフォス

@KilianFothそれは良いですか?
C.ロス

反復的な方法と反復的な開発方法論を関連付けます。より良い用語は高階関数だと思います。(余談ですが、メソッドは戻り値の型/ voidを持ちませんが、関数は何かを返します)。
ヨハネスルドルフ

@JohannesRudolph「メソッドには戻り値の型/ボイドはありませんが、関数は何かを返します」-どの言語でこれは本当ですか?私はこれを聞いたことがありません。実際、私が聞いた唯一の本当の違いは、メソッドがクラスに関連付けられていることであり、関数はそうではありません。
threed

回答:


14

私の推測では、複雑さを分割/移動しただけです。.where()CCの実装をカウントしないため、減少しました。

全体的なCCは実際には移動していませんが、フレームワークのコードに移動したため、独自のコードのCCは減少しました。

保守性が高いと思います。言語の機能である場合は、それを使用します。あなたが使用しているのは「ああ、わかりました、巧妙なトリック」ではなく、単純なインラインのような関数だけです。


11

あなたがしているのは、メトリックとして循環的複雑さの欠陥を強調することだけです。コードの複雑さは実際には変更されていません。最初の例には明示的な分岐があり、2番目の例には暗黙の分岐があることを理解する必要があります。2番目の構文は、構文を理解している場合、および問題となる可能性のある基本的な構文をあまり使用しないため、より明確でわかりやすくなります。


1
whereここではフレームワークに由来する可能性が高いため、ここでは自分のコードの循環的な複雑さが軽減されていると主張することができます。関数をいくつかに分割してもシステムの全体的な複雑さは減りませんが(実際、わずかに増加するだけであることが多いので)、メトリックとしての循環的複雑さはここでも有用だと思います。システムの一部が過度に複雑になり、分解する必要があります。
threed

6

客観的に質問に答えるためには、保守性のための何らかのメトリックが必要です。循環的複雑度自体は、メンテナンス性の尺度ではないが、それはある旨が保守性を測定することを、いくつかのメトリックのコンポーネント。たとえば、保守性指数の式は次のとおりです。

MI = 171 - 5.2 * ln(V) - 0.23 * (G) - 16.2 * ln(LOC)

G問題のコードの循環的な複雑さはどこにありますか。したがって、コードの一部の循環的複雑度を減らすことは、定義上、コードの保守可能性インデックスと同様に循環的複雑度を使用する他のメトリックを改善します

あなたが提案する変更の種類がプログラムをプログラマにとってより保守しやすいように見せるかどうかを言うのは難しいです。それはおそらく(あなたの場合)whereメソッドにどれだけ慣れているかに依存します。


警告:マジックナンバー!(>_<)
イズカタ

保守性インデックスには、この小さな問題が1つだけあります。その3つのコンポーネントは、Halstead Volume、Cyclomatic Complexity、およびLines of Codeです。Halstead VolumeとCyclomatic Complexityはどちらも、Lines of Codeと非常に強い相関関係があることが示されています。これは、コード行のみに依存する保守性指標の代替近似式を導き出すことができ、計算式の困難性がほとんどなく、元とほぼ同じ精度になることを意味します。
ジョンR.ストローム

@ JohnR.Strohm保守性メトリックとしてLOCを使用しただけでも、同様の結果が得られると思います。OPの変更によりLOCが4から1に減り、循環的複雑度を減らす他の変更も同様にLOCを減らす可能性があります。いずれにしても、それは本当に保守性をどのように定義し測定するかにかかっています。
カレブ

1
@カレブ:同意した。私がしようとするポイントは、これらの本当に複雑なメトリックとメトリックの組み合わせに人々が頻繁に行くことであり、それらのすべてが実際のコードと単純な古いコードの行(LOC)と強く相関していることが示されていることに気づかないことです、したがって、LOCよりも予測的または記述的な値はありません。
ジョンR.ストローム

3

循環的複雑度(CC)はコードサイズと非常に強く相関していることが示されているため、「CCには独自の説明力がないと言えるほど」。あなたが本当に求めているのは、「C#、JavaScript、(できれば)Java 8のような現代言語によく見られるような反復メソッドが、コードの理解可能性とサポート可能性へのコードサイズの影響を減らすかどうか」です。

その時点で、答えが明白になることを望むでしょう。一般に、コードを短くすると理解、保守、およびサポートが容易になることが何十年も知られています。


2

Cyclomatic Complexityの生の統計について話しているなら、確かに。2から0に変更しました。数字を使用する場合は、純粋なゲインを使用します。

しかし、実際の(人間の)観点からは、実際に複雑さを2 増やしたと主張します。その1つのポイントは、他のプログラマーが流understandな構文LINQの知識を持ってこれを理解する必要があることですコード。

難易度を上げるもう1つのポイントは、ラムダ式を理解することです。この例では、ラムダはかなり単純ですが、それらを完全に評価するにはパラダイムシフトがいくつか必要です。

この場合、使用することa.where(x => matches(x, arg))はひどくはありません。正直に言って、同僚にLINQとLambdaの式を初めて表示して操作させるのに最適な方法です(実際にこれを使用して、以前の同僚にLINQ / Lambdaそして、他のコードのセットは、非常に効果的です。)ただし、これらの使用にはある程度の知識が必要です。

LINQリファクタリングが実際にそのforeachループになるものよりも読むのが著しく悪い場合を見たことがあるので、注意することをお勧めします。


1

主観的には、開発者がラムダ式を理解しているかどうかに依存します。

List<String> filteredList = originalList.where(s => matches(s));

理解が速く、おそらく少し簡単です。私はsとmatches()の使用にもっと関心があるでしょう。どちらも自己記述的ではありません。

List<String> filteredList = 
    originalList.where(stringToBeTested => matchesNameTest(stringToBeTested));

または

List<String> filteredList = 
        originalList.where(originalListString => matchesNameTest(originalListString));

開発者により多くの意味のある情報を提供し、matches()関数に飛び込んでどのマッチが実行されているかを判断することなく、分析が容易になります。

保守性とは、コードを理解する能力だけでなく、主にコードを理解できる速度と精度に関するものです。


2
こんにちはエース。これは、構造を強調するためのセマンティックコンテンツを意図的に欠いている例です。
C.ロス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.