【问题标题】:Would creating a method "template" (by passing some method to the template method) be considered bad design?创建一个方法“模板”(通过将一些方法传递给模板方法)会被认为是糟糕的设计吗?
【发布时间】:2011-01-01 05:15:01
【问题描述】:

我很难确定一个好的标题,因此如有必要,请随时更改。我不太确定如何描述我想要实现的目标,我想到了“模板”这个词(显然我不是在尝试使用 C++ 模板)。

如果我有一个类在每个方法中执行一些操作,让我们假装做一个 try/catch 和其他一些东西:

public class SomeService
{
    public bool Create(Entity entity)
    {
        try
        {
            this.repository.Add(entity);
            this.repository.Save();
            return true;
        }
        catch (Exception e)
        {
            return false;
        }
    }
}

然后我添加另一个方法:

public bool Delete(Entity entity)
{
    try
    {
        this.repository.Remove(entity);
        this.repository.Save();
        return true;
    }
    catch (Exception e)
    {
        return false;
    }
}

这里的方法显然有一个模式:try/catch 与返回值。所以我在想,既然服务上的所有方法都需要实现这种工作模式,我是否可以将它重构为这样的东西:

public class SomeService
{
    public bool Delete(Entity entity)
    {
        return this.ServiceRequest(() =>
        {
            this.repository.Remove(entity);
            this.repository.Save();
        });
    }

    public bool Create(Entity entity)
    {
        return this.ServiceRequest(() =>
        {
            this.repository.Add(entity);
            this.repository.Save();
        });
    }

    protected bool ServiceRequest(Action action)
    {
        try
        {
            action();
            return true;
        }
        catch (Exception e)
        {
            return false;
        }
    }
}

这样,所有方法都遵循相同的“模板”执行。这是一个糟糕的设计吗?请记住,try/catch 并不是每种方法都可能发生的。考虑添加验证,每个方法都需要说if(!this.Validate(entity))...

这是否太难维护/丑陋/糟糕的设计?

【问题讨论】:

  • 您确定要阻止异常执行其工作吗?
  • @Dialectus - 请参阅我今天之前的帖子,了解应该从服务方法返回的值:stackoverflow.com/questions/4570717/… 请记住,这只是一个示例,它可能是其他一些方法“模板”。

标签: c#


【解决方案1】:

使用 lambda 表达式通常会降低可读性。这基本上意味着几个月后有人会读到这里并感到头疼。

如果没有必要,或者没有真正的性能优势,只需使用 2 个单独的函数。 IMO 最好有可读的代码然后使用漂亮的技术。

【讨论】:

  • 但是假设“模板”在未来发生了显着变化,并且 100 方法使用相同的模板,这需要大量重构/复制/粘贴。我正在尝试遵循 DRY 原则。
  • @TheCloudlessSky,另一个要记住的原则是 YAGNI;现代重构工具使以后更改变得不那么繁琐,因此现在保持简单,仅在必要时进行更改。
  • @Dan - 非常好的观点。假设事情确实进入了“模板”变得相当复杂的复制/粘贴状态,你有什么建议?
  • @TheCloudlessSky,在这种情况下,如果重复确实成为一个问题,我认为你的方法是有道理的。我在服务隔离之类的事情上使用了类似的方法(例如,您可以通过委托/lambda 使用 WCF 代理接口(以便执行正确的错误处理),但您无法获得对直接使用实时代理。)
【解决方案2】:

这似乎是一种仅限于小的“动作”的技术——“动作”中的代码越多,它的用处就越少,因为可读性会越来越受到损害。实际上,您在这里真正重用的唯一东西是 try/catch 块,这首先可以说是糟糕的设计。

这并不是说它一定是一种糟糕的设计模式,只是您的示例似乎并不适合它。例如,LINQ 广泛使用这种模式。结合扩展方法和流畅的风格,它可以非常方便并且仍然保持可读性。不过,再一次,它似乎最适合替换小的“动作”——除了几行代码之外的任何东西,我认为它会变得相当混乱。

如果您打算这样做,您可能希望通过传入动作和动作用作参数的实体而不只是动作来使其更有用。这将使您更有可能在您的操作中进行额外的常见计算。

 public bool Delete( Entity entity )
 {
      return this.ServiceRequest( e => {
          this.repository.Remove( e );
          this.repository.Save();
      }, entity );
 }

 protected bool ServiceRequest( Action<Entity> action, Entity entity )
 {
      try
      {
          this.Validate( entity );
          action( entity );
          return true;
      }
      catch (SqlException) // only catch specific exceptions
      {
          return false;
      }
 }

【讨论】:

    【解决方案3】:

    我会尝试寻找一种方法来从存储库更改刷新(保存)中中断存储库操作(添加/更新/删除)。 根据您使用代码(web/windows)的方式,您可能可以为此使用“会话”管理器。这种分离还允许您在单个事务中刷新多个操作。

    其他人认为,与主题无关,而是与代码有关,不要返回真/假,而是让异常通过或返回一些可以让您区分原因或失败的东西(验证等)。您可能希望抛出合同违约(传递的无效数据)并返回正常无效业务规则的值(不使用异常作为业务规则,因为它们很慢)。

    【讨论】:

    • 它目前将与 MVC(因此是 Web)一起使用......但我确实喜欢有某种管理会话的想法。另外,如果您今天看到我以前的帖子,您就会知道我确实想知道返回真/假stackoverflow.com/questions/4570717/… 谢谢!
    【解决方案4】:

    另一种方法是创建一个界面,例如 IExample,它表达了您的更多意图并将动作与参与者分离。因此,在最后的示例中,您可能提到使用 this.Validate(entity)。显然,这不适用于您当前的设计,因为您必须传递动作和实体,然后将实体传递给动作。

    如果您将其表示为实体上的接口,您只需传递任何实现 IExample 的实体。

    公共接口示例 { 布尔验证(); 无效动作(); }

    现在实体实现了 IExample,ServiceRequest 现在将 IExample 作为其参数,并且 bob 是你的叔叔。

    您的原始设计一点也不差,它实际上很常见,但随着需求的变化而变得限制(这次动作必须调用两次,这一次它需要调用验证,然后是后验证)。通过通过接口表达它,您可以使其可测试,并且可以将模式移动到旨在重放此特定序列的帮助器类。一旦提取,它也变得可测试。这也意味着如果需求突然需要调用后验证,您可以重新审视整个设计。

    最后,如果该模式没有被大量应用,例如您可能在班级中有三个位置,那么可能不值得努力,只需编写每个长手。有趣的是,由于事情是 Jitted 的方式,拥有三个不同且完整的方法而不是三个共享一个共同核心的方法可能会更快......

    【讨论】:

      猜你喜欢
      • 2016-04-26
      • 1970-01-01
      • 1970-01-01
      • 2012-01-19
      • 2015-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-14
      相关资源
      最近更新 更多