技巧以重构具有许多分支的代码段(如果/则/否则) [英] Tricks to refactor a piece of code with many branches (if/then/else)

查看:73
本文介绍了技巧以重构具有许多分支的代码段(如果/则/否则)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很难尝试重构带有许多分支的某些代码.有很多if/then/else块,其中一些是嵌套的.

I'm having a hard time trying to refactor some pieces of code with many branches. There are many if/then/else blocks, some of them are nested.

在不浪费大量时间首先尝试理解功能的每个次要方面的情况下,有什么技巧可以用来重构代码吗?

Are there any tricks that can be used to refactor the code without wasting a lot of time trying to understand every minor aspect of the functionality first?

目前,我基本上是在使用布尔代数(De Morgan的定律).我正在尝试修改if语句中的条件,以便可以在if/then/else块之外弹出小的代码段.这在某种程度上有帮助,但是代码仍然保持分支.我知道在某个时候我最终将不得不将大类分解为较小的类方法,但这很复杂,因为代码包含对许多其他类方法的调用,并且具有许多局部作用域变量,因此我将不得不处理传递许多关于新方法的争论.我想知道在我将其分解成不同的较小部分(类方法)之前是否可以使用其他技巧来提高代码质量.

For now I'm basically using boolean algebra (De Morgan's laws). I'm trying to modify the conditions in if statements so I could pop up small code snippets outside if/then/else blocks. This helps somehow, but the code still remains branched. I know at some point I will eventually have to break the biggies into smaller class methods, but this is complicated because the code contains calls to many other class methods and has a lot of local-scope variables, so I will have to deal with passing many arguments to new methods. I wonder If I could use some other tricks to improve the code quality before I start breaking it into distinct smaller pieces (class methods).

推荐答案

以我的经验,在功能上分解(将事情分解为能做一件事并做得很好的较小函数)之前几乎总是比较有效的进入分支和等效逻辑表达式.

In my experience, it's almost always more productive to functionally decompose (break things into smaller functions that do one thing and do it well) before getting into branches and equivalent logical expressions.

而又没有浪费很多时间来首先了解功能的每个次要方面?

without wasting a lot of time trying to understand every minor aspect of the functionality first?

除其他事项外,功能分解的行为将使您了解代码的作用,从而导致进一步分解的机会.您想要使用仅包含几个参数的函数,并且不返回,不包含一个或几个值.

Among other things, the act of functionally decomposing will lead to your understanding what the code does, which leads to further chances to decompose. You want to get to functions that take only a few arguments, and return no, one or few values.

通过分解,您将更好地了解哪些部分在算法上是必不可少的(必须以这种方式进行才能正确)以及哪些部分仅是实现细节(必须完成,但可以通过多种方式完成)不同的方式而不更改输出).

And by decomposing, you'll gain a better understanding of what parts are algorithmically essential (it must be done this way to be correct) and what parts are merely implementation details (which must be done, but can be done in various different ways without changing the output).

通常,您会发现许多深度嵌套的if是不必要的,或者是重复重复的,或者足够类似,如果您将其他方面的细微差异排除在外,则可以简化为一个或几个函数重复的代码.

Often, you'll find that many of those deeply nested ifs are either unnecessary, or are repeated redundantly, or are similar enough they can be reduced to one or a few functions if you factor out minor differences in otherwise repeated code.

换句话说,您想要获得的句柄不是代码的细节,而是代码正在处理的抽象事物.许多问题归结为某种数据结构(列表,队列,树,集合)和对该结构的某些操作.在一定程度上,您可以梳理数据结构和算法,从而获得简化事物的抽象水平.或者,您会发现您所做的任何事情都更适合于其他结构或算法,然后您就可以省去很多麻烦了.

In other words, you want to get a handle not on the minutia of the code, but the abstract thing the code is dealing with. Many many problems boil down to some data structure (a list, a queue, a tree, a set) and some operation(s) on that structure. To the extent that you can tease apart data structures and algorithms, you can gain a level of abstraction that simplifies things. Or you can discover that whatever you have to do is better suited to other structures or algorithms, and then you can blow away a lot of cruft.

我记得几年前,一个总是写数据库游标的同事,可以做任何事情,因为这就是他所知道的全部方法.设置和删除游标需要一些样板,以及需要进行一些错误检查的循环,因此他的代码总是看起来很复杂.他会在存储的proc中进行操作,这当然会增加更多样板.

I remember years ago a colleague who always wrote database cursors, to do anything, because that's all he knew how to do. Setting up and tearing down a cursor requires some boilerplate, and a loop with some error checking, so his code always looked superficially complicated. He'd do it in a stored proc, which of course added more boilerplate.

现在,它发生在Sybase T-SQL中,它具有一个用于错误的全局变量和一个用于游标状态的全局变量,他都要检查两次,一次是在获得第一个游标行时一次,然后是一次遍历所有其他行的循环.并且他始终将错误变量(@@error)与游标状态变量(@@sqlstatus混淆,该状态变量在成功时为0(获取游标行),在失败时为1,在游标中没有更多行时为2). .他的代码在标称路径上起作用,因为如果要一行,它们都为零,并且当他尝试在最后一行之后获取该行时,他会得到一个错误.因此,如果您仔细查看他的代码,您会(吟(再次!)并为他解决该问题.

Now, as it happened, this was in Sybase T-SQL, which has a global for errors and a global for cursor status, both of which he'd check twice, once when he got the first cursor row, then once in a loop that iterated over all the other rows. And he consistently confused the error variable (@@error) with the cursor state variable (@@sqlstatus, which is zero on success (got a cursor row), 1 on failure, and 2 if there are no more rows in the cursor). His code worked on the nominal path, because both were zero if he'd gotten a row, and when he tried to get the row after the last one, he'd get an error. So if you looked closely at his code, you'd groan (once again!) and fix that for him.

但是随后您可以看得更近一些,看到他正在像在所有行where x = 1的集合中进行游标操作,并为每个行设置y = y * 2进行游标,然后您最终告诉他,只要写更新声明!".

But then you'd look closer, and see that he was doing something like cursoring through the set of all rows where x = 1 and for each row setting y = y * 2, and you'd end up telling him, "just write an update statement!".

您对全局变量进行检查的更正,而正确却没有解决真正的问题,那就是他没有充分的理由使用游标和存储的proc,而他本来可以从客户端代码发送了一条更新语句.

Your correcting of his checking of globals, while correct, didn't address the real problem, which was that he was using cursor and a stored proc for no good reason, when he just could have sent an update statement from the client code.

弄清楚这一点更加困难,因为您不仅要查看全局变量在本地的使用,还必须查看两个地方:声明游标的位置(declare cursor_foo cursor for select * from table where x = 1 for update;)和发生更新的位置的下二十行(update table set y = y * 2 where current of cursor_foo). (所有这些都将以非常简单的方式多行显示.)

Figuring that out was more difficult, because instead of just looking at the local use of the globals, you had to look two places: where the cursor was declared (declare cursor_foo cursor for select * from table where x = 1 for update;) and twenty lines down where the update happened (update table set y = y * 2 where current of cursor_foo). (And all of these would be multi-line in a very boilerplate way.)

弄清楚这一点也更加困难,因为您只是假设没有人会为了更新而经历所有这些事情. 肯定所有这些样板都必须是因为更新中发生了一些特别的事情,对吗?还是因为where谓词是动态的?因此,您应该看看这个,并且是一个谦虚的编码员,尊重他的同事,您的第一个直觉是," I 在这里缺失了什么,一定有一个 reason 使用了光标吗?"

Figuring it out was also more difficult because you just assumed no one would go through all this just to do an update; surely all this boilerplate had to be because something special was happening in the update, right? Or because the where predicate was dynamic or something? So you'd look at this, and being a modest coder who respects his colleagues, your first instinct would be, "what am I missing here, there must be a reason a cursor was used?"

(尽管我和他的老板都在解释@@ error和@@ sqlstatus之间的区别,他只是从来没有得到它,更不用说他几乎总是可以做一个update的想法;他在程序方面进行了思考代码,尽管拥有数据库程序员"的丰富经验,但从不获取"数据库.)

(Despite both me and his boss explaining the difference between @@error and @@sqlstatus, he just never got it, much less the idea that he could almost always just do an update; he thought in terms of procedural code, and never "got" databases, despite ostensible experience as a "database programmer".)

这个教训是,直到您确认一开始就需要细节(实施细节)之后,才可以开始学习细节.通过首先分解代码,您有更大的机会了解正在处理的抽象,并真正改善了代码.

The lesson is to not get caught up in minutia until you have confirmed there's a need for the minutia (the implementation details) in the first place. By decomposing your code first, you have a better chance of understanding the abstractions you're dealing with, and really improving the code.

这篇关于技巧以重构具有许多分支的代码段(如果/则/否则)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆