3

我有一个 WCF 服务,其中包含许多(~30)这样的方法:

public Foo GetFooById(string id)
{
    try
    {
        return FooLogic.GetById(id);
    }
    catch (Exception ex)
    {
        throw LogAndThrowFaultException(ex);
    }
}

除了 try 块中的一行之外,这些方法中的代码完全相同。这很简单,我什至已经抽象了日志记录并抛出异常。

本着 DRY 的精神,我可以走得更远,这样做:

public Foo GetFooById(string id)
{
    return PerformServiceOperation<Foo>(() => FooLogic.GetById(id));
}

为此,此方法将处理重复的 try/catch 代码并调用每个函数:

private T PerformServiceOperation<T>(Func<T> func)
{
    try
    {
        return func.Invoke();
    }
    catch (Exception ex)
    {
        throw LogAndThrowFaultException(ex);
    }
}

这是不是太过分了?代码是否已经如此简单,是否应该不理会它?还是将 func 传递给辅助方法并让该方法处理重复的 try/catch 是个好主意?我也关心可读性。我认为对辅助方法的调用有点难看。

4

3 回答 3

4

我不得不说,总的来说,在生产代码中我倾向于认为 DRY 应该被带到一个极端。我有几个原因我已经发展了几十年。

1)在一个地方的“修复”得到传播。您建议的像这样的简单修复可以节省数小时的质量检查,因为一个又一个方法无法通过简单的测试。这可能是一个极端的情况,但我已经看到它发生了。此外,尽管您知道此代码被复制并且您必须在多个地方进行修复,但下一位工程师是否知道这一点?

顺便说一句,一个可行的替代方法是记录每个位置的代码被复制以引用其他位置,以便维护者知道去哪里。

2) 程序员能力。通过坚持绝对 0 代码重复,您将学习使您成为更好工程师的技术。我见过很多代码,工程师坚持认为他们不能让它变得更干——但我自己已经做了十几次(也许使用了你在这里使用的技巧),因为我'我不得不在某个时候把它弄明白。

3) 可能是最不明显但最重要的——迭代重构。重构通常只能分层进行。你肯定看过一堆乱七八糟、难以理解的代码,并且只看到了最次要的重构——但在重构之后,其他的变得清晰,那些使其他的成为可能。我采取了每个人都不敢触及的巨大方法,并以这种方式将它们缩小到原始大小的 1/10。

4)数据提取。如果你坚持 DRY,你最终会得到很多批次的数据,因为最基本的重构之一就是从相似的代码中提取不同的数据,然后组合代码。这使您的代码更易于维护——在过去,我经常能够通过更改字符串数组来解决问题,而在重构之前通常需要数小时或数天。

我不得不让人们相信我去过的任何地方(你可以通过查看对这个问题说“不”的人数来了解这一点)。Extreme DRY 一开始通常很难卖——直到他们看到结果。我认为你个人能做的最重要的事情就是尽可能地保持干​​爽(随着你的练习,这会变得更多)。

如果您列出的特定重构结果导致问题,那么您已经学到了一些东西,并且下次可以以不同的方式处理它。你学会了,时间没有浪费。

顺便说一句,您将了解到以其他人可以理解和使用的方式编写极其干燥的代码至关重要——这并不总是容易的,并且可能导致一些人讨厌“重构”和“过度工程” “直到他们发现他们正在编写的代码的人在沟通方面不够好(通过代码)。

PS:我应该提到另一个——因为编码 DRY 很有趣。作为程序员,复制和粘贴代码(并修复代码)是您可以做的最痛苦的工作,但是创建仍然可用和可维护的简短、干燥的代码是一项心理挑战,任何称职的工程师都应该享受这个难题。

于 2013-06-04T20:04:31.863 回答
2

从概念上讲,我根本不认为这太过分了。只改变一次处理异常的方式,你会很高兴你决定不一遍又一遍地输入相同的 try-catch 块。我更倾向于通过一个方面来处理异常,但你的解决方案也是可行的。

于 2013-06-04T02:43:55.390 回答
0

走得太远?

请注意,您在每个方法中添加了对 PerformServiceOperation() 的调用,以便将异常处理移出方法。除非您从重复的异常处理中节省大量维护时间,否则您可能会走得很远。

但是,如果已经有一种方法可以将异常处理添加到您选择的框架 (WCF) 以处理未处理的异常,那么这样做不会太远。

使用面向方面编程(AOP)来记录异常的方法拦截非常简单。但是仅仅因为这个原因,切换到 AOP 肯定是太远了……

于 2013-06-04T02:52:35.460 回答