程序员应该使用布尔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进来。