概括代码内部的变量用法


11

我想知道对变量进行泛化是否是一个好习惯(使用单个变量存储所有值)。
考虑简单的例子

 Strings querycre,queryins,queryup,querydel; 
    querycre = 'Create table XYZ ...';
    execute querycre ;
    queryins = 'Insert into XYZ ...';
    execute queryins ;
    queryup  = 'Update  XYZ set ...';
    execute queryup;
    querydel = 'Delete from XYZ ...';
    execute querydel ;

 Strings query; 
    query= 'Create table XYZ ... ';
    execute query ;
    query= 'Insert into XYZ ...';
    execute query ;
    query= 'Update  XYZ set ...';
    execute query ;
    query= 'Delete from XYZ ...';
    execute query ;

在第一种情况下,我使用4个字符串(每个字符串存储数据)来执行其后缀中提到的操作。
在第二种情况下,只有1个变量可以存储各种数据。
具有不同的变量可以使其他人更容易阅读和更好地理解它。但是拥有太多它们使管理变得困难。

变量太多也会影响我的表现吗?

PS:请不要在示例中回答代码,这只是为了传达我的真正意思。


当然,您重复使用了相同的变量...因为您已经在函数中定义了它。那就是功能。
zzzzBov 2012年

Answers:


26

不得不问自己这个问题是一种很强烈的气味,您没有遵循DRY(不要重复自己)。假设您使用假设的花括号语言进行了以下操作:

function doFoo() {
    query = "SELECT a, b, c FROM foobar WHERE baz = 23";
    result = runQuery(query);
    print(result);

    query = "SELECT foo, bar FROM quux WHERE x IS NULL";
    result = runQuery(query);
    print(result);

    query = "SELECT a.foo, b.bar FROM quux a INNER JOIN quuux b ON b.quux_id = a.id ORDER BY date_added LIMIT 10";
    result = runQuery(query);
    print(result);
}

将其重构为:

function runAndPrint(query) {
    result = runQuery(query);
    print(result);
}

function doFoo() {
    runAndPrint("SELECT a, b, c FROM foobar WHERE baz = 23");
    runAndPrint("SELECT foo, bar FROM quux WHERE x IS NULL");
    runAndPrint("SELECT a.foo, b.bar FROM quux a INNER JOIN quuux b ON b.quux_id = a.id ORDER BY date_added LIMIT 10");
}

请注意,不再需要决定是否使用不同的变量,以及现在如何更改在一个位置运行查询和打印结果的逻辑,而不必重复应用相同的修改三次。(例如,您可能决定要通过模板系统泵送查询结果,而不是立即打印)。


2
我只喜欢DRY原则:)
artjom 2012年

1
@tdammers函数内只有两行代码是件好事吗?请考虑我是否具有此函数doFoo(){print(runQuery(“ XYZ的Selct a,b,c”));}
Shirish11 2012年

1
不,调用堆栈不会增加-每次调用都runAndPrint将在推入一个堆栈帧时将其推入,然后在函数退出时将其弹出回去。如果您调用它三遍,它将执行三对推/弹出对,但是堆栈一次最多不会增长一帧以上。您只应该真正担心带有递归函数的调用堆栈深度。
tdammers 2012年

3
只需两行代码的函数就可以了:如果两行组成一个逻辑单元,那么两行就可以了。我编写了很多单线函数,只是为了将一些信息隔离在一个地方。
tdammers 2012年

1
@JamesAnderson:这是一个有些人为的例子,但是它可以说明一个观点。这与您拥有多少行代码无关。您陈述相同事实的次数是多少次。这就是DRY左右,还有单一来源真相的原则,你不可复制粘贴规则,等等
tdammers

14

通常,这是一个不好的做法。

以这种方式重用变量会使代码难以理解。

那些阅读代码的人不会期望变量会以这种方式被重用,并且不会知道为什么在开始时设置的值在函数末尾会有不同的值。

您发布的示例非常简单,并没有真正受此问题困扰,但是它们并不代表某些确实可以重用变量的代码(在开始时设置变量,在中间的某个地方重用-看不见)。

您提供的示例将自己封装到函数中,您将在其中传递查询并执行它。


受其影响的系统性能如何?
Shirish11年

@ Shirish11-可能会。取决于编译器,语言,环境和其他变量。
奥德

通常,编译器擅长对此进行优化。但是,它始终取决于编译器/平台/特定情况/配置。
deadalnix 2012年

7

自记录代码更易于阅读和维护

遵循“最少惊讶原则”和“ 按文档编制代码 ”的戒律:为一个目标使用一个变量,以使其易于理解且易于阅读而无需解释。

正确结构化的代码更容易(因此更便宜)(重新)使用

同样,这里似乎query总是被用来在执行之前准备一条语句。这可能是您要将此代码的一部分重构为一个(或多个)帮助器方法以准备和执行查询(以符合DRY原则)的信号。

这样,您将有效地:

  • 在您的辅助方法中仅使用一个变量来标识当前上下文的查询,
  • 每次您要重新执行查询时,只需输入较少的代码,
  • 使您的代码更具可读性。

例子:

从您的示例中考虑,重构版本显然更好。当然,您的摘录仅是解决此问题的一个示例,但是这个概念仍然适用且正确。

您的示例1:

Strings querycre,queryins,queryup,querydel; 
    querycre = 'Create table XYZ ...';
    execute querycre ;
    queryins = 'Insert into XYZ ...';
    execute queryins ;
    queryup  = 'Update  XYZ set ...';
    execute queryup;
    querydel = 'Delete from XYZ ...';
    execute querydel ;

您的示例2:

 Strings query; 
    query= 'Create table XYZ ...';
    execute query ;
    query= 'Insert into XYZ ...';
    execute query ;
    query= 'Update  XYZ set ...';
    execute query ;
    query= 'Delete from XYZ ...';
    execute query ;

示例3(重构的伪代码):

def executeQuery(query, parameters...)
    statement = prepareStatement(query, parameters);
    execute statement;
end

// call point:
executeQuery('Create table XYZ ... ');
executeQuery('Insert into XYZ ...');
executeQuery('Update  XYZ set ...');
executeQuery('Delete from XYZ ...');

定期重用将显示出好处。

个人轶事

我最初是作为一名C程序员从事有限的屏幕空间工作的,因此重新使用变量对于编译后的代码(当时)是有意义的,并且允许一次读取更多代码。

但是,在转而使用高级语言并精通函数式编程之后,我养成了在不加限制的情况下尽可能使用不可变变量和不可变引用的习惯。

对我有什么好处?

如果您习惯于使所有函数的输入都是不可变的,并且要返回新结果(就像真正的数学函数那样),那么您就会养成不重复存储的习惯。

通过扩展,这导致:

  • 您编写短函数,
  • 有明确的目标
  • 更容易理解
  • 重复使用
  • 扩展(无论是通过OO继承还是通过功能链),
  • 和文件(已经是自记录文件)。

我并不是说这里的可变状态没有任何好处,我只是在指出这种习惯可能会如何发展以及如何影响代码的可读性。


2

在代码设计方面

通常,重用变量来存储不同的值没关系-毕竟,这就是为什么将它们称为变量的原因,因为存储在变量中的值会有所不同- 只要该值不仅具有相同的类型而且还具有相同的含义。例如,当然可以在currentQuery这里重用变量:

for currentQuery in queries:
    execute query;

自然存在一个循环,因此您必须重用一个变量,但是即使没有循环,也可以。如果该值不是同一意思,请使用单独的变量。

但是,具体来说,您所描述的代码看起来不太好-会重复。最好使用循环或辅助方法调用(或两者都使用)。我个人很少见过生产代码看起来像您的第一个或第二个版本,但就我而言,我认为第二个版本(可变重用)更为普遍。

在表现方面

它取决于所使用的语言,编译器和运行时系统,但通常不会有任何区别 -特别是基于堆栈的寄存器机的编译器(如流行的x86 / x86-64)无论如何只是使用任何可用的堆栈内存或将其注册为分配目标,完全忽略您是否需要相同的变量。

例如,gcc -O2生成完全相同的二进制文件,而我所知道的唯一性能差异是编译期间符号表的大小-除非您回到60年代,否则完全可以忽略不计。

Java编译器将生成字节码,该字节码在第一个版本中需要更多的存储空间,但是JVM的抖动无论如何都会将其删除,因此,我再次怀疑,即使您需要高度优化的代码,也几乎不会对性能产生明显影响。


0

我认为重用该变量在大多数情况下都可以。

对我来说,我大部分时间只是简单地重用查询变量。我几乎总是在此之后执行查询。当我不立即执行查询时,我通常使用不同的变量名。


-1

如果您的编译器特别笨拙,则可能会增加堆栈使用率。我个人认为每个查询都没有单独的变量会增加任何可读性,您仍然需要实际查看查询字符串以了解其功能。


我刚刚提供了一个简单的示例,以便读者更容易理解我的追求。我的代码比这复杂得多。
Shirish11年

-2

在示例中,我将继续第二个示例。读者和优化人员都非常清楚自己在做什么。第一个示例更合适一些,我会使用更复杂的代码,但是要这样做:

{
    String query = 'Create table XYZ ...';
    execute query;
}
{
    String query = 'Insert table XYZ ...';
    execute query;
}
And so on...

(此时,我可能会考虑使用tdammers的解决方案。)

第一个示例的问题在于,querycre整个块的范围可能很大。这会使阅读代码的人感到困惑。它还可能会使优化器感到困惑,优化器可能会留下不必要的内存写操作,因此querycre以后可以在需要时使用(不是必需的)。如果使用大括号,query则仅将其存储在寄存器中。

对于“创建表”和“执行”这样的短语,在我看来,这似乎并不会引起额外的内存写入,因此,我只不过是将代码弄乱了读者。但它很方便的知道这一点,如果你写的代码,其中速度确实事。


我不同意。如果为了清楚起见而喜欢第二个示例,则应将其重构为连续调用helper方法。它可以传达更多的含义,并且需要更少的代码。
haylem 2012年

@haylem:在一个真正的简单案例中,就像这样,您要添加一个辅助方法,阅读代码的人必须去寻找它。(而且有人可能会对helper方法感到麻烦,因此必须弄清楚调用它的所有位置。)不太清楚,大约是相同数量的代码。在更复杂的情况下,我会选择解决方案,然后是tdammer。我回答这个问题的主要目的是指出未充分利用的变量对人员和优化人员都造成的((虽然晦涩,但有趣))问题。
RalphChapin 2012年

@haylem:您和tdammer都提供了正确的解决方案。我只是认为在某些情况下可能会过大。
RalphChapin 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.