5

現在のプロジェクトでは、コード メトリクスの「保守性インデックス」と「サイクロメティック複雑度」の目標を設定しました。保守性指数は 60 以上で、循環的複雑度は 25 以下である必要があります。保守性指数 60 以上はかなり高い値であることがわかっています。

また、エンティティをフィルタリング/グループ化/選択するために多くの linq を使用します。これらの linq クエリは、Maintainability Index でそれほど高く評価されていないことがわかりました。このクエリを拡張メソッドに抽象化することで、より高い保守性インデックスが得られます。これは良いことです。しかし、ほとんどの場合、拡張メソッドはもはやジェネリックではありません。これは、ジェネリック型の代わりに自分の型で拡張メソッドを使用するためです。

たとえば、次の linq-query と拡張メソッドの比較:

Linq クエリ

List.Where(m => m.BeginTime >= selectionFrom && m.EndTime <= selectionTo)

延長方法:

public static IEnumerable<MyType> FilterBy(this IEnumerable<MyType> source, DateTime selectionFrom, DateTime selectionTo)
{
    return (IEnumerable<MyType>)source.Where(m => m.BeginTime >= selectionFrom && m.EndTime <= selectionTo);
}

List.FilterBy(selectionFrom, selectionTo);

拡張メソッドにより、Maintainability Index が 6 ポイント向上し、流暢な構文が得られます。一方、静的クラスを追加する必要がありますが、それは一般的ではありません。

どのようなアプローチが有利になるかについてのアイデアはありますか? または、linq クエリをリファクタリングして保守性インデックスを改善する方法について、別のアイデアをお持ちですか?

4

5 に答える 5

5

メトリックのためにクラスを追加するべきではありません。メトリクスはコードを改善するためのものですが、盲目的にルールに従うと、最良のルールであっても、実際にはコードに害を及ぼす可能性があります

特定の保守性と複雑さのインデックスに固執するのは良い考えではないと思います。古いコードを評価する場合、つまり、プロジェクトを継承し、その複雑さを見積もる必要がある場合に役立つと思います。ただし、点数が足りないからと言ってメソッドを抽出するのはばかげています。

そのようなリファクタリングがコードに価値を追加する場合にのみリファクタリングしてください。 このような価値は、数字では表現できない複雑な人間の測定基準であり、それを推定することはまさにプログラミング経験に関するものです。つまり、最適化、可読性、きれいな API 、クールなコード、シンプルなコード、迅速な出荷、一般化、仕様などのバランスを見つけることです。

これはあなたが従うべき唯一の指標ですが、誰もが同意する指標であるとは限りません...

EnumerableExtensionsあなたの例では、同じ LINQ クエリが何度も使用されている場合、 inExtensionsフォルダーを作成してそこに抽出することは完全に理にかなっています。ただし、1 回か 2 回使用された場合、または変更される可能性がある場合は、詳細なクエリの方がはるかに優れています。

また、やや否定的な意味合いを持つ一般的ではないとあなたが言う理由もわかりません. どこでもジェネリックは必要ありません。実際、拡張メソッドを作成するときは、他のクラスのメソッド セットを汚染しないように、選択できる最も具体的な型を考慮する必要があります。ヘルパーに のみを使用させたい場合IEnumerable<MyType>は、 の拡張メソッドを正確に宣言することは恥ずべきことではありませんthis IEnumerable<MyType>。ところで、あなたの例には冗長なキャストがあります。それを取り除く。

忘れないでください、ツールは愚かです。私たち人間もそうです。

于 2011-06-08T08:50:50.073 に答える
4

あなたへの私のアドバイスは...あなたの指標の奴隷にならないでください!これらは機械で生成されたものであり、ガイダンスとしてのみ使用することを意図しています。熟練した経験豊富なプログラマーの代わりになることは決してありません。

あなたのアプリケーションにはどちらが適していると思いますか?

于 2011-06-08T08:46:37.610 に答える
2

私は、拡張メソッドの戦略に同意します。いくつかの実世界のアプリで問題なく使用しました。

私にとっては、メトリックだけでなく、そこでのコードの再利用性も重要です。次の疑似例を参照してください。

var x = _repository.Customers().WhichAreGoldCustomers();

var y = _repository.Customers().WhichAreBehindInPayments();

これら 2 つの拡張メソッドを持つことで、メトリックの目標が達成され、「ゴールド カスタマーとは何かを定義するための 1 つの場所」も提供されます。「ゴールド カスタマー」と連携する必要があるときに、さまざまな開発者がさまざまな場所でさまざまなクエリを作成する必要はありません。

さらに、これらは構成可能です。

var z = _repository.Customers().WhichAreGoldCustomers().WhichAreBehindInPayments();

私見これは勝利のアプローチです。

私たちが直面した唯一の問題は、拡張メソッドの Intellisense が狂ってしまう ReSharper のバグがあることです。「.Whic」と入力すると、必要な拡張メソッドを選択できますが、「タブ」を押すと、選択した拡張メソッドではなく、まったく異なるものがコードに挿入されます。このためにReSharperからの切り替えを検討しましたが...いや:)

于 2011-06-08T12:29:48.043 に答える
1

いいえ: この場合、私は循環的複雑性を無視します - あなたが最初に持っていたものはより優れていました.

より説明的なものは何かを自問してください。これ:

List.Where(m => m.BeginTime >= selectionFrom && m.EndTime <= selectionTo)

またはこれ:

List.FilterBy(selectionFrom, selectionTo);

最初のものはあなたが望むものを明確に表現していますが、2番目のものはそうではありません. 「FilterBy」が何を意味するかを知る唯一の方法は、ソース コードを調べてその実装を調べることです。

クエリ フラグメントを拡張メソッドに抽象化することは、クエリ フラグメントが何を行っているかを一目で判断するのが容易ではない、より複雑なシナリオでは理にかなっています。

于 2011-06-08T09:22:00.037 に答える
0

私はこの手法をいくつかの場所で使用しました。たとえば、クラス Payment には、Payments のドメイン固有の拡張機能を提供する対応するクラス PaymentLinqExtensions があります。

あなたが与える例では、よりわかりやすいメソッド名を選択します。範囲が包括的か排他的かという問題もありますが、それ以外の場合は問題ないようです。

システムに複数のオブジェクトがあり、日付を持つという概念が一般的である場合は、インターフェース、おそらく IHaveADate (またはもっと良いもの :-) を検討してください。

public static IQueryable<T> WithinDateRange(this IQueryable<T> source, DateTime from, DateTime to) where T:IHaveADate

(IQueryable は興味深いものです。IEnumerable がそれにキャストできるとは思えません。これは残念なことです。データベース クエリを使用している場合は、サーバーに送信される最終的な SQL にロジックを表示できます。これは良いことです。すべての LINQ には、期待どおりにコードが実行されないという潜在的な落とし穴があります)

アプリケーションで日付範囲が重要な概念であり、範囲が "EndDate" の最後の午前 0 時に開始するか、開始時の午前 0 時に開始するかについて一貫性を持たせる必要がある場合は、DateRange クラスが役立つ場合があります。それで

public static IQueryable<T> WithinDateRange(this IQueryable<T> source, DateRange range) where T:IHaveADate

また、必要に応じて提供することもできます

public static IEnumerable<T> WithinDateRange(this IEnumerable<T> source, DateRange range, Func<DateTime,T> getDate)

しかし、これは DateRange と関係があるように感じます。状況によって異なりますが、どの程度使用されるかはわかりません。一般的すぎると理解しにくくなり、LINQ のデバッグが難しくなることがわかりました。

var filtered = myThingCollection.WithinDateRange(myDateRange, x => x.Date)
于 2011-06-08T14:14:14.460 に答える