添加参数还是创建新方法?

5

假设我有一个长期维护的代码库,就像这样:

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

它已经存在很长时间了,GetDonuts方法实现了它所说的功能。但有一天,我需要添加一个新屏幕来显示数据库中的所有甜甜圈,结果发现该方法有一个隐藏功能 - 它会过滤掉所有的 stale=true 的甜甜圈。但是在我的新屏幕上,我想展示全部甜甜圈,包括不新鲜的!这里应该采取什么最好的方法呢?
假设这个方法在许多地方都被使用,并且默认行为需要保持不变,是最好添加一个名为GetAllDonuts的新方法来取消过滤,还是只需在GetDonuts方法上添加一个onlyFresh参数?
我猜这只是判断的问题,但我想知道是否有更明智的答案?

如果你的接口被你无法控制和更改的代码使用,那么你不能更改这个接口。相反,应该添加一个新的接口来实现第一个接口,新的客户端应该使用新的接口。 - Richard Anthony Freeman-Hein
1
接口中的方法签名不能有访问修饰符,它们总是公共的。 - Richard Anthony Freeman-Hein
5个回答

10

我会重载这个方法,创建一个新的重载版本,接受参数 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);
}

谢谢!我不太担心实现,只是公共接口应该如何看起来... - GoatInTheMachine
1
问题在于,在这两种情况下,实现IDonutRepository接口的任何类都必须进行更改。如果您可以访问实现接口的代码,那么这是可以接受的,但否则,这样做就行不通了。在客户端实现您的接口的情况下,您不能更改接口。您必须创建一个新的接口IDonutRepository2,它实现IDonutRepository。 - Richard Anthony Freeman-Hein
@Richard - 如果OP仅仅是提供接口给客户端实现的话,那么这是正确的情况。然而,在OP的情况下,很容易推断出他也控制着代码(或者至少有能力强制功能更改),否则这根本不会成为一个问题。 - Justin Niessner
是的,这正是我可以访问所有代码并可以更改任何我喜欢的内容的情况,因为在这种情况下,正确的做法似乎最不明显。 - GoatInTheMachine

2
为什么不使用可选参数呢?这样就不会破坏现有的代码:
interface IDonutRepository
{
    public IEnumerable<Donut> GetDonuts(bool onlyFresh);
}

实现:

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

1

根据情况,可能需要考虑引入一个访问甜甜圈的属性。

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

如果您正在使用像Entity Framework或NHibernate这样的Linq-savvy ORM,那么实现此接口就相当容易。

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

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

1

这实际上取决于个人喜好,某种程度上...

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

话虽如此,如果保持兼容性至关重要,则采用带有额外参数的方法重载可能是未来最佳选择。(这在使用此API的人和地方方面存在很大差异...)


一般来说,除非有充分的理由,否则更改签名是不好的。这样做可能会破坏很多代码,并使使用您的类的其他开发人员感到沮丧。只需确保非常好地注释事物,即默认行为是不包括陈旧的甜甜圈,并添加带有布尔参数的新方法。 - MonkeyWrench
@MonkeyWrench:就像我说的,这取决于具体情况——然而,长远来看,重构以使签名更清晰通常是一个不错的改变。虽然这可能会很痛苦,而且肯定有避免它的理由,但如果整个情景都在你的控制之下,我认为这仍然是最好的选择。 - Reed Copsey

0
软件设计中越来越流行的趋势之一是将接口与实现分离。这个原则是将模块分成公共部分和私有部分,以便您可以更改私有部分而不需要与其他模块协调。然而,还有一个进一步的区别——公共接口和发布接口之间的区别。这个区别很重要,因为它影响您如何使用接口。

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


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