【问题标题】:Readability vs. Maintainability: Condensing statements to loops可读性与可维护性:将语句压缩为循环
【发布时间】:2013-12-30 03:41:11
【问题描述】:

首先,我指的是一个例子:

UINT f, i, s;
CONST UINT k[5] = { VK_LBUTTON, VK_RBUTTON, VK_MBUTTON, VK_XBUTTON1, VK_XBUTTON2 };

for (f = RI_MOUSE_LEFT_BUTTON_DOWN, i = 0, s = RI_KEY_MAKE; f != RI_MOUSE_WHEEL; f <<= 1, i += s, s = !s)
    if ((pMouseData->usButtonFlags & f) == f)
        SetVKeyState(k[i], s);

与此相比:

if (pMouseData->usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN)
    SetVKeyState(VK_LBUTTON, RI_KEY_MAKE);

if (pMouseData->usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP)
    SetVKeyState(VK_LBUTTON, RI_KEY_BREAK);

if (pMouseData->usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN)
    SetVKeyState(VK_RBUTTON, RI_KEY_MAKE);

if (pMouseData->usButtonFlags & RI_MOUSE_RIGHT_BUTTON_UP)
    SetVKeyState(VK_RBUTTON, RI_KEY_BREAK);

if (pMouseData->usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN)
    SetVKeyState(VK_MBUTTON, RI_KEY_MAKE);

if (pMouseData->usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_UP)
    SetVKeyState(VK_MBUTTON, RI_KEY_BREAK);

if (pMouseData->usButtonFlags & RI_MOUSE_X1_BUTTON_DOWN)
    SetVKeyState(VK_XBUTTON1, RI_KEY_MAKE);

if (pMouseData->usButtonFlags & RI_MOUSE_X1_BUTTON_UP)
    SetVKeyState(VK_XBUTTON1, RI_KEY_BREAK);

if (pMouseData->usButtonFlags & RI_MOUSE_X2_BUTTON_DOWN)
    SetVKeyState(VK_XBUTTON2, RI_KEY_MAKE);

if (pMouseData->usButtonFlags & RI_MOUSE_X2_BUTTON_UP)
    SetVKeyState(VK_XBUTTON2, RI_KEY_BREAK);

后面的代码显然是一个完全展开的循环。

从长远来看,这确实是一个可读性减少冗余(如代码不同区域的重复语句)是否更好的问题。

我更关心稍后在大型代码库的上下文中访问它。当然减少要修改的区域数量是一件好事; for 循环的初始化和更新语句是否有点混乱?

有没有人遇到过这个问题,可能是微不足道的问题。

【问题讨论】:

  • 这里的邮政编码。不要把我们送到别处。
  • 如果你想要可读性,你可以从重命名变量开始
  • 不,我的意思是可读性。展开的循环很容易阅读,因为它是显式的,并且不使用逗号技巧之类的东西来在小空间中使用多个语句,但是如果需要更改语句,则可能会出错,因为要更改的内容太多。我意识到我的名字并不惊人;他们不应该成为焦点。这是将多个变量推入循环以压缩展开版本的想法。
  • 快速浏览 - 展开的逐行代码立即可以理解,易于修改和调试。这对我足够了。我做了很多维护/增强和“聪明”的代码,需要努力解开只是让我想扼杀编写它的白痴。
  • @MartinJames 抱歉,不能 +2

标签: c performance winapi readability maintainability


【解决方案1】:

关键是只压缩完全相似的语句:

const UINT k[5] = { VK_LBUTTON, VK_RBUTTON, VK_MBUTTON, VK_XBUTTON1, VK_XBUTTON2 };

static_assert( (RI_MOUSE_LEFT_BUTTON_DOWN << 2) == RI_MOUSE_RIGHT_BUTTON_DOWN );

for ( UINT i = 0; i < _count_of(k); ++i ) {
    if (pMouseData->usButtonFlags & (RI_MOUSE_LEFT_BUTTON_DOWN << 2*i))
        SetVKeyState(k[i], RI_KEY_MAKE); 
    if (pMouseData->usButtonFlags & (RI_MOUSE_LEFT_BUTTON_UP << 2*i)))
        SetVKeyState(k[i], RI_KEY_BREAK); 
}

所有逗号运算符都消失了,异常循环增量消失了,符号变量仍然用于键状态。

我觉得这其实比原文更容易阅读,因为它适合一页代码,而且重复很明显。

编辑:现在标志关系假设已记录在案。

我实际上可能会:

struct { UINT vk;      UINT downflag;                 UINT upflag;
} const k[] = {
       { VK_LBUTTON,   RI_MOUSE_LEFT_BUTTON_DOWN,     RI_MOUSE_LEFT_BUTTON_UP },
       { VK_RBUTTON,   RI_MOUSE_RIGHT_BUTTON_DOWN,    RI_MOUSE_RIGHT_BUTTON_UP },
    ...
};
for ( UINT i = 0; i < _count_of(k); ++i ) {
    if (pMouseData->usButtonFlags & k[i].downflag)
        SetVKeyState(k[i].vk, RI_KEY_MAKE); 
    if (pMouseData->usButtonFlags & k[i].upflag)
        SetVKeyState(k[i].vk, RI_KEY_BREAK); 
}

为了消除关于以正确顺序使用相邻位的标志的假设。

可以使用后一个版本将SetVKeyState 的第二个参数作为表列之一,但 IMO 会丢失有价值的配对结构。

【讨论】:

  • “关键是只浓缩完全相似的陈述”这句话事后看来确实很明显,但它确实消除了我的困惑。
  • @Alex:在您提供的代码中,显然存在成对语句的模式。所以循环版本应该尽量保留这种模式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-02
  • 1970-01-01
相关资源
最近更新 更多