JavaScript中的有条件函数链

10
我正在尝试重构以下node.js代码。
每个情况都会生成缩略图,并将不同的GraphicMagic转换链接到图像上。
switch(style.name) {
    case 'original':
        gm(response.Body)
            .setFormat('jpg').autoOrient().resize(style.w, style.h, style.option)
            .toBuffer(function(err, buffer) { if (err) { next(err); } else { next(null, buffer); } });
        break;
    case 'large':
        gm(response.Body)
            .setFormat('jpg').autoOrient().resize(style.w, style.h, style.option)
            .quality(style.quality)
            .strip().interlace('Plane')
            .toBuffer(function(err, buffer) { if (err) { next(err); } else { next(null, buffer); } });
        break;
    case 'medium':
        gm(response.Body)
            .setFormat('jpg').autoOrient().resize(style.w, style.h, style.option)
            .crop(style.w, style.h, style.crop.x_offset, style.crop.y_offset)
            .repage('+')
            .strip().interlace('Plane')
            .toBuffer(function(err, buffer) { if (err) { next(err); } else { next(null, buffer); } });
        break;
    case 'small':
        gm(response.Body)
            .setFormat('jpg').autoOrient().resize(style.w, style.h, style.option)
            .crop(style.w, style.h, style.crop.x_offset, style.crop.y_offset).repage('+')
            .quality(style.quality)
            .strip().interlace('Plane')
            .toBuffer(function(err, buffer) { if (err) { next(err); } else { next(null, buffer); } });
        break;
}

然而,所有情况的开头和结尾都有一些相同的转换,因此有重构的空间。我尝试用以下方法进行重构,但代码似乎不正确:

gm(response.Body)
.setFormat('jpg').autoOrient().resize(style.w, style.h, style.option, function(err, response) {
    if (style.name === 'original'){
        return response;
    } else if (style.name === 'large'){
        return response.quality(style.quality)
    } else if (style.name === 'medium'){
        return response.crop(style.w, style.h, style.crop.x_offset, style.crop.y_offset).repage('+')
    } else if (style.name === 'small'){
        return response.crop(style.w, style.h, style.crop.x_offset, style.crop.y_offset).repage('+').quality(style.quality)
    }
}).(function(response) {
    return (stryle.name !== 'original') ? response.strip().interlace('Plane') : return response;
}).(function(argument) {
    return response.toBuffer(function(err, buffer) { if (err) { next(err); } else { next(null, buffer); } });
});

1
这个似乎很奇怪。 - Roko C. Buljan
另外,对于一个更易读的对象 {"original":resp1, "normal":resp2, etc...},您怎么看? - Roko C. Buljan
哦,我在第一个片段中没有看到resize接受function(err, response)回调函数? - Bergi
2个回答

11

我不会选择使用switch

这里根本没有理由使用链式语句。只需执行:

// if (!/^(original|large|medium|small)$/.test(style.name)) throw new Error(…);
var x = gm(response.Body)
         .setFormat('jpg')
         .autoOrient()
         .resize(style.w, style.h, style.option);
if (style.name == "medium" || style.name == "small")
    x = x.crop(style.w, style.h, style.crop.x_offset, style.crop.y_offset)
         .repage('+');
if (style.name == "large" || style.name == "small")
    x = x.quality(style.quality);
if (style.name == "large" || style.name == "medium" || style.name == "small")
// possibly better than if (style.name != "original")
    x = x.strip()
         .interlace('Plane');
x.toBuffer(next);

但是,如果你有一个很大的选项集,以至于它变得难以阅读,最好将每个转换分解为一个函数:

function resizedJpg(x) {
    return x.setFormat('jpg').autoOrient().resize(style.w, style.h, style.option);
}
function cropped(x) {
    return x.crop(style.w, style.h, style.crop.x_offset, style.crop.y_offset).repage('+');
}
function withQuality(x) {
    return x.quality(style.quality);
}
function stripped(x) {
    return x.strip().interlace('Plane');
}

然后分别应用它们:

({
    original: [resizedJpg],
    large:    [resizedJpg,          withQuality, stripped],
    medium:   [resizedJpg, cropped,              stripped],
    small:    [resizedJpg, cropped, withQuality, stripped]
}[style.name]).reduce(function(x, trans) {
    return trans(x);
}, gm(response.Body)).toBuffer(next);

感谢您提供这么好的回答!第一种方法中,由于我们没有将所有方法链接在一起并使用了一个中间变量 x,所以它会单独运行 gm 转换(在 if 语句之前运行一次,之后运行两次),还是当我们调用 toBuffer() 时会一次性完成所有转换? - Sbbs
我的担忧是多次调用转换会在图像上多次运行gm,因此在一次链接所有转换并仅在图像上运行gm一次时计算成本更高。 - Sbbs
1
@SBBS:不,只要链同步执行,gm 无法区分它是否被分配给变量,这种构建器模式的工作方式应该是你调用 .toBuffer() 时才会执行。如果代码写得好,你可以在同一时间调用两次 .toBuffer() ,它会正好运行两次。 - Bergi

5
我为这种情况编写了一个小的JavaScript助手:https://github.com/sms-system/conditional-chain 使用此工具,您可以根据条件可选地链接方法。代码将更加可控和易读。
cond(gm(response.Body)
  .setFormat('jpg')
  .autoOrient()
  .resize(style.w, style.h, style.option))

.if(style.name == 'medium' || style.name == 'small', gm => gm
  .crop(style.w, style.h, style.crop.x_offset, style.crop.y_offset)
  .repage('+'))

.if(style.name == 'large' || style.name == 'small', gm => gm
  .quality(style.quality))

.if(style.name != 'original', gm => gm
  .strip()
  .interlace('Plane'))

.end().toBuffer(next)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接