程序员应该使用布尔variables来“logging”他们的代码吗?

我正在阅读McConell的Code Complete ,他讨论了使用布尔variables来logging你的代码。 例如,而不是:

if((elementIndex < 0) || (MAX_ELEMENTS < elementIndex) || (elementIndex == lastElementIndex)){ ... } 

他build议:

 finished = ((elementIndex < 0) || (MAX_ELEMENTS < elementIndex)); repeatedEntry = (elementIndex == lastElementIndex); if(finished || repeatedEntry){ ... } 

这是我的逻辑,良好的做法和非常自我logging。 然而,我很犹豫,开始使用这种技术,因为我几乎从来没有碰到过。 也许只是因为稀less而会令人困惑。 然而,我的经验还不是很多,所以我有兴趣听听程序员对这种技术的看法,我很想知道是否有人经常使用这种技术,或者在阅读代码时经常看到这种技术。 这是一个值得采用的习俗/风格/技巧吗? 其他程序员是否会理解并欣赏它,或者认为它很奇怪?

将一个嵌套过于复杂的expression式分解为局部variables的简单子expression式,然后将其重新组合,这是一种相当普遍和stream行的技术 – 与子expression式和/或整体expression式是布尔型还是几乎任何其他types。 用精心挑选的名字,这种有品位的分解可以提高可读性,一个好的编译器应该不会产生与原来的复杂expression式等价的代码。

一些没有“赋值”本身概念的语言,比如Haskell,甚至引入了专门的结构来让你使用“为子expression式命名”技术(Haskell中的where子句) – 似乎是说明一些受欢迎的技术问题! – )

我已经使用它,虽然通常包装布尔逻辑到一个可重用的方法(如果从多个位置调用)。

它有助于可读性,当逻辑改变时,只需要在一个地方改变。

其他人会理解它,不会觉得奇怪(除了那些只写千行function的人,就是这样)。

我尽量做到这一点。 当然,你正在使用“额外的”代码,但同时你正在描述你为什么要比较两个值。

在你的例子中,我看着代码,并问自己:“好的,为什么看到这个值小于0? 在第二个中,你清楚地告诉我,当发生这种情况时,一些进程已经完成。 第二个猜测你的意图是什么。

对我来说最重要的是当我看到一种方法,如: DoSomeMethod(true); 为什么它会自动设置为true? 它更像可读性

 bool deleteOnCompletion = true; DoSomeMethod(deleteOnCompletion); 

提供的样本:

 finished = ((elementIndex < 0) || (MAX_ELEMENTS < elementIndex)); repeatedEntry = (elementIndex == lastElementIndex); if(finished || repeatedEntry){ ... } 

也可以重写使用提高可读性和保留布尔逻辑的方法(正如Konrad指出的那样):

 if (IsFinished(elementIndex) || IsRepeatedEntry(elementIndex, lastElementIndex)){ ... } ... private bool IsFinished(int elementIndex) { return ((elementIndex < 0) || (MAX_ELEMENTS < elementIndex)); } private bool IsRepeatedEntry(int elementIndex, int lastElementIndex) { return (elementIndex == lastElementIndex); } 

它的价格当然是两个额外的方法。 如果你这样做了很多,它可能会使你的代码更具可读性,但是你的类不那么透明。 但是,再一次,你也可以把额外的方法移到帮助类中。

唯一的办法,我可以看到这是错误的是如果布尔片段没有一个名字是有道理的,并select一个名字。

 //No clue what the parts might mean. if(price>0 && (customer.IsAlive || IsDay(Thursday))) => first_condition = price>0 second_condition =customer.IsAlive || IsDay(Thursday) //I'm still not enlightened. if(first_condition && second_condition) 

我指出了这一点,因为制定像“注释所有代码”,“对所有超过3个部分的if-criteria使用已命名的布尔值”这样的规则是很平常的,只是为了获得在语义上为空的评论

 i++; //increment i by adding 1 to i's previous value 

通过做这个

 finished = ((elementIndex < 0) || (MAX_ELEMENTS < elementIndex)); repeatedEntry = (elementIndex == lastElementIndex); if(finished || repeatedEntry){ ... } 

你从你的大脑中删除逻辑 ,并把它放在代码中。 现在程序知道你的意思。
每当你命名一些东西时,你就会给它一个物理的表示 它存在。
你可以操纵和重用它。

您甚至可以将整个块定义为谓词:

 bool ElementBlahBlah? (elementIndex, lastElementIndex); 

并在该function中做更多的事情(以后)。

如果expression式是复杂的,那么我将它移动到另一个函数返回一个bool例如isAnEveningInThePubAGoodIdea(dayOfWeek, sizeOfWorkLoad, amountOfSpareCash)或重新考虑代码,以便这样一个复杂的expression式是不需要的。

我认为最好是创build函数/方法而不是临时variables。 这种方式可读性也增加,因为方法变得更短。 Martin Fowler的书“重构”对于提高代码质量有很好的build议。 与您的特定示例相关的重构称为“用查询replace温度”和“提取方法”。

我个人认为这是一个很好的做法。 这对代码执行的影响是微乎其微的,但是如果使用得当,清晰度对于稍后维护代码是非常有用的。

请记住,这样你计算超过必要的。 由于从代码中取出条件,你总是计算它们两个(没有短路)。

以便:

 if((elementIndex < 0) || (MAX_ELEMENTS < elementIndex) || (elementIndex == lastElementIndex)){ ... } 

改造后:

 if((elementIndex < 0) || (MAX_ELEMENTS < elementIndex) | (elementIndex == lastElementIndex)){ ... } 

在大多数情况下不是问题,但仍然在某些情况下可能意味着更糟糕的performance或其他问题,例如,在第二个expression式中,假设第一个expression式失败。

如果方法需要成功的通知:(在C#中的例子)我喜欢使用

 bool success = false; 

开始。 代码是一个失败,直到我改变它:

 success = true; 

然后在最后:

 return success; 

我想,这取决于你/你的球队喜欢什么风格。 “引入variables”重构可能是有用的,但有时不:)

在他之前的post中,我应该不同意凯文的观点。 他的例子,我想,可以的情况下,当引入variables可以改变,但引入它只有一个静态布尔是无用的,因为我们在方法声明中有参数名称,所以为什么在代码中复制它?

例如:

 void DoSomeMethod(boolean needDelete) { ... } // useful boolean deleteOnCompletion = true; if ( someCondition ) { deleteOnCompletion = false; } DoSomeMethod(deleteOnCompletion); // useless boolean shouldNotDelete = false; DoSomeMethod(shouldNotDelete); 

根据我的经验,我经常回到一些旧的脚本,并想知道我到底在想什么? 例如:

 Math.p = function Math_p(a) { var r = 1, b = [], m = Math; a = m.js.copy(arguments); while (a.length) { b = b.concat(a.shift()); } while (b.length) { r *= b.shift(); } return r; }; 

这不像以下那样直观:

 /** * An extension to the Math object that accepts Arrays or Numbers * as an argument and returns the product of all numbers. * @param(Array) a A Number or an Array of numbers. * @return(Number) Returns the product of all numbers. */ Math.product = function Math_product(a) { var product = 1, numbers = []; a = argumentsToArray(arguments); while (a.length) { numbers = numbers.concat(a.shift()); } while (numbers.length) { product *= numbers.shift(); } return product; }; 

我很less创build单独的variables。 当testing变得复杂时,我所做的就是嵌套IF并添加注释。 喜欢

 boolean processElement=false; if (elementIndex < 0) // Do we have a valid element? { processElement=true; } else if (elementIndex==lastElementIndex) // Is it the one we want? { processElement=true; } if (processElement) ... 

这种技术的缺陷是,下一位程序员可能会改变逻辑,但不会更新注释。 我想这是一个普遍的问题,但我已经有很多次,我看到一个评论,说:“validation客户ID”,下一行是检查部分号码或一些这样的,我只是想知道客户ID进来。