5

在我当前的项目中,我们为代码度量“可维护性索引”和“Cyclometic Complexity”设定了一些目标。可维护性指数应为60或更高,Cyclometic的复杂性为25或更低。我们知道60和更高的可维护性指数是相当高的。我们还使用了很多linq来过滤/分组/选择实体。我发现这些linq查询在可维护性指数上的得分并不高。 把这个查询抽象成扩展方法给了我一个更高的可维护性指数,这是很好的。但在大多数情况下,扩展方法不再是通用的,因为我将它们与我的类型(而不是泛型)一起使用。您是否将您的LINQ查询抽象为扩展方法

例如,下面的LINQ查询VS扩展方法:

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

扩展方法给我维修性指标6分的改进,并给出了一个很好的流利的语法。 另一方面,我不得不添加一个静态类,它不是通用的。

关于什么方法有什么想法会有你的青睐?或者,对于如何重构linq查询来改进可维护性索引,可能有不同的想法?

+3

度量标准仅仅是一种度量,但从来不是一个目标。 – 2011-06-08 09:01:46

+0

我对任何一个无法解释的黑盒子的维修性指数都持怀疑态度。至少循环复杂性可以确定实际测量的内容。你知道它是否解释过[无]解释[这里](http://msdn.microsoft.com/en-us/library/bb385914.aspx)? – AakashM 2011-06-08 10:04:05

回答

5

为了度量标准,您不应该添加类。任何指标都意味着让您的代码更好但盲目遵循以下规则,即使是最好的规则,实际上可能会损害您的代码

我不认为坚持某些可维护性和复杂性指标是一个好主意。我相信它们可用于评估旧代码,即当您继承项目并需要估计其复杂性时。然而,提取方法是很荒谬的,因为你没有得到足够的分数。

只有重构如果这样的重构增加了代码的价值。这种是数字的复杂的人类指标无表情,并估计它到底是什么编程经验优化之间VS可读性VS干净的API VS酷码有关调查平衡VS简单的代码VS快速送货vs概括vs规格等

这是你应该遵循的唯一指标,但它并不总是度量每个人都同意在...

至于你的榜样,如果相同的LINQ查询被反复使用,它可以完美的创建EnumerableExtensionsExtensions文件夹中并将其解压。但是,如果它使用了一次或两次,或者可能会发生变化,详细查询就会更好。

我也不明白你为什么说他们不是通用的有一些负面的内涵。你无需到处使用泛型!事实上,在编写扩展方法时,应该考虑最具体的类型,以免污染其他类的方法集。如果你希望你的帮手只能使用IEnumerable<MyType>,那么在宣布this IEnumerable<MyType>的扩展方法时完全没有什么可耻的。顺便说一句,在你的例子中有冗余铸造。摆脱它。

不要忘记,工具是愚蠢的。人类也是如此。

4

我对你的建议是...... 不要成为你的指标的奴隶!它们是机器生成的,仅用于指导。他们永远不会成为熟练的有经验的程序员的替代者。

您认为正确的您的应用有哪些?

+0

也许我的问题使得它看起来像我们被奴役的指标,情况并非如此。我们正在使用它作为指导,但也将自己的目标定为高,以确保每个人都认为如何使您的方法尽可能清洁和可维护。我对使用什么有一些想法,但希望看到别人对此有何看法。 – ChristiaanV 2011-06-08 08:54:09

1

否:在这种情况下,我会忽略圈复杂性 - 你原来最好是更好。

问问你自己什么更具说明性。这:

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

或本:

List.FilterBy(selectionFrom, selectionTo); 

第一个明确表示自己想要什么,而第二个则没有。了解“FilterBy”意味着什么的唯一方法是进入源代码并查看它的实现。

将查询片段抽象为扩展方法对于更复杂的场景很有意义,在这种情况下,查看片段正在做什么并不容易。

+0

我同意你的立场;只是要注意,根据这个问题,这种改变明显地提高了*可维护性指数*,而不是圈复杂度。 – AakashM 2011-06-08 10:02:17

2

我一致同意扩展方法策略。我在一些真实世界的应用程序中使用它没有问题。

对我来说,它不仅是指标,还有代码的可重用性。请参阅下面的伪例子:

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

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

有了这两个扩展方法,实现你的目标,指标,它也提供了“一个地方的是什么是黄金客户的定义。”不同开发人员在需要与“黄金客户”合作时,不会在不同的地方创建不同的查询。

此外,它们是可组合:

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

恕我直言,这是一个成功的做法。

我们遇到的唯一问题是有一个ReSharper错误,有时扩展方法的Intellisense变得疯狂。你输入“.Whic”,它可以让你选择你想要的扩展方法,但是当你“选项卡”时,它会在代码中放入完全不同的东西,而不是你选择的扩展方法。我曾考虑从ReSharper切换为此,但...nah :)

+0

没错,那也是我喜欢的。 – ChristiaanV 2011-06-09 05:24:37

0

我已经在地方使用了这种技术,例如一个类Payment有一个相应的类PaymentLinqExtensions,它为Payments提供特定于域的扩展。

在给出的例子中,我会选择一个更具描述性的方法名称。还有一个问题是范围是包容性的还是排他性的,否则它看起来不错。

如果系统中有多个对象进行有日期的概念被普遍然后再考虑接口,也许IHaveADate(或更好的东西:-)

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

(IQueryable的是有趣的。我不如果你正在处理数据库查询,那么它可以允许你的逻辑出现在发送到服务器的最终SQL中,这是很好的。 LINQ表示你的代码在你期望的时候不会被执行)

如果日期范围在你的应用程序中是一个重要的概念并且您需要对范围是从“EndDate”的结尾午夜开始还是在开始的午夜开始保持一致,那么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) 

,但这对我来说感觉更是与日期范围。我不知道它会被使用多少,尽管你的情况可能会有所不同。我发现变得过于通用会让事情变得难以理解,而LINQ可能很难调试。

var filtered = myThingCollection.WithinDateRange(myDateRange, x => x.Date)