【问题标题】:Add a parameter, or create a new method?添加参数,还是创建新方法?
【发布时间】:2011-03-23 17:44:54
【问题描述】:

假设我有一个像这样建立已久的存储库:

interface IDonutRepository
{
    public IEnumerable<Donut> GetDonuts();
}

它已经存在很长时间了,GetDonuts 方法正如它所说的那样。然后有一天我需要添加一个新屏幕来显示数据库中的所有甜甜圈,结果发现该方法有一个隐藏的功能——它过滤掉了stale = true 的所有甜甜圈。但是在我的新屏幕上,我想展示所有这些,甚至是陈旧的!这里最好的方法是什么?

假设这个方法在所有地方都使用,并且默认行为需要保持不变,最好添加一个名为 GetAllDonuts 的新方法,它不进行过滤,还是我应该添加一个将onlyFresh 参数添加到GetDonuts 方法中?

我猜这只是判断,但我想知道那里是否有更明智的答案?

【问题讨论】:

  • 如果您的界面被您无法控制且无法更改的代码使用,那么您不得更改界面。相反,添加一个实现第一个接口的新接口,新客户端应该使用新接口。
  • 附注接口中的方法签名不能有访问修饰符。它们始终是公开的。

标签: c# oop design-patterns


【解决方案1】:

我将重载该方法,创建一个采用showStale 参数的新重载,然后修改旧方法以使用传递false 参数值的新重载。

界面如下:

interface IDonutRepository
{
    public IEnumerable<Donut> GetDonuts();
    public IEnumerable<Donut> GetDonuts(bool showStale);
}

或者,如果您使用的是 .NET 4.0,则可以使用可选参数:

interface IDonutRepository
{
    public IEnumerable<Donut> GetDonuts(bool showStale = false);
}

【讨论】:

  • 谢谢!不过,我不太担心实施,只是公共界面应该看起来如何......
  • 问题在于,在这两种情况下,任何实现 IDonutRepository 的类都必须更改。如果您可以访问实现接口的代码,那很好,但否则,这将不起作用。在客户端实现您的接口的情况下,您不得更改接口。您必须创建一个新的 IDonutRepository2,它实现了 IDonutRepository。
  • @Richard - 这是真的,如果 OP 只是将接口提供给客户端来实现,情况就是如此。但是,在 OP 的情况下,很容易推断出他也可以控制代码(或者至少有能力强制更改功能),否则这甚至都不是问题。
  • 是的,这只是我可以访问所有代码并且可以更改我喜欢的任何内容的情况,因为在这种情况下,正确的事情似乎最不明显。
【解决方案2】:

为什么不使用可选参数?这样您就不会破坏现有代码:

interface IDonutRepository
{
    public IEnumerable<Donut> GetDonuts(bool onlyFresh);
}

实施:

public IEnumerable<Donut> GetDonuts(bool onlyFresh = false)
{
    if (onlyFresh)
        // do stuff
    else
        // do other stuff
}

【讨论】:

    【解决方案3】:

    在某种程度上,这真的归结为个人喜好......

    如果您有能力更改 API,我会(个人)重命名当前方法,使其明显不会返回所有 Donut 实例。我的期望是存储库的GetDonuts 方法将获得所有甜甜圈。您可以自行决定通过参数或其他名称来执行此操作。

    话虽如此,如果保持兼容性至关重要,采用额外参数的方法重载可能是前进的最佳选择。 (这在很大程度上取决于使用此 API 的人员和位置...)

    【讨论】:

    • 一般来说,除非有充分的理由,否则更改签名是一件坏事。您可以通过这种方式破坏大量代码,并使使用您的类的其他开发人员感到沮丧。只要确保很好地评论事情,默认行为是不包括陈旧的甜甜圈,并使用 bool 参数添加新方法
    • @MonkeyWrench:正如我所说,这取决于场景 - 但是,从长远来看,将签名更改为更清晰的反应通常是一个很好的更改。这可能会很痛苦,而且绝对有理由避免它,但如果整个场景都在你的控制之中,它通常仍然是 IMO 的最佳选择。
    【解决方案4】:

    根据具体情况,可以考虑引入一个属性来访问甜甜圈。

    interface IDonutRepository
    {
        IEnumerable<Donut> Donuts { get; }
        .. or ..
        IQueryable<Donut> Donuts { get; }
    }
    

    如果您使用的是 Entity Framework 或 NHibernate 之类的 Linq 精通 ORM,那么实现此接口相当容易。

    旧的 GetDonuts 方法可以重命名为 GetFreshDonuts(),或者您可以将对其的调用重构为以下形式:

    repository.Donuts.Where(x => !x.Stale)
    

    【讨论】:

      【解决方案5】:

      软件的发展趋势之一 设计将界面与 执行。原理是关于 将模块分成公共和 私人部分,以便您可以更改 私处不协调 与其他模块。然而,有 进一步的区别—— 公共和发布的接口。这个 区别很重要,因为它 影响您如何使用 界面。

      http://www.martinfowler.com/ieeeSoftware/published.pdf

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-08
        • 1970-01-01
        • 1970-01-01
        • 2015-07-19
        • 1970-01-01
        • 2017-10-29
        • 1970-01-01
        相关资源
        最近更新 更多