如何使用相同的公开接口扩展/定义接口?

3

我希望在更新类中的接口时,能够尽可能地减少代码更改的次数。

我知道可以“只需创建一个新接口”来提供新功能,但这样一来,我就必须更新代码中所有旧接口的引用以使用新接口。

我想知道这是否比创建一个新接口并更新所有引用更好。

假设我从这里开始:

public interface iMyWaiter : iMyWorker
{
    void takeOrder();
}

当然,我的类看起来像这样,由于它们与手头的主题无关,我不会展示类中的所有方法和属性。

public class MyWaiter : MyWorker, iMyWaiter
{
}

现在假设我想要将iMyWaiter更新到版本2,通常的方法是创建一个新的接口:

public interface iMyWaiter2 : iMyWaiter
{
    void getBill();
}

并从新接口派生出类:

public class MyWaiter : MyWorker, iMyWaiter2
{
}

这将导致搜索 iMyWaiter 的引用并将其替换为 iMyWaiter2 的成本。

iMyWaiter waiter = new MyWaiter();
waiter.takeOrder();

现在变成:

iMyWaiter2 waiter = new MyWaiter();
waiter.takeOrder();

我正在考虑制作一个通用的“iWaiter”接口,它派生自版本化的接口,并且有一个从版本化接口派生的通用类:
public interface iMyWaiter_1 : iMyWorker
{
    void takeOrder();
}
public class MyWaiter_1 : MyWorker, iMyWaiter_1
{
}


public interface iWaiter : iMyWaiter_1
{
}
public class MyWaiter : MyWaiter_1, iMyWaiter
{
}

现在,当我需要更新我的界面时,我只需在通用界面和上一个版本之间插入新界面即可:

public interface iMyWaiter_1 : iMyWorker
{
    void takeOrder();
}
public class MyWaiter_1 : MyWorker, iMyWaiter_1
{
}


public interface iMyWaiter_2 : iMyWaiter_1
{
    void getBill();
}
public class MyWaiter_2 : MyWaiter_1, iMyWaiter_2
{
}

基本上这个接口和类总是可以创建,我的代码不会改变,除非使用新接口中公开的新功能。
public interface iWaiter : iMyWaiter_2
{
}
public class MyWaiter : MyWaiter_2, iMyWaiter
{
}

现在,我只需将“getBill”函数添加到我的代码中,不必担心接口问题。
iMyWaiter waiter = new MyWaiter();
waiter.takeOrder();
waiter.getBill();

我的问题:

这是一个好的设计实践吗?

有哪些缺点?

我没有考虑什么?


等待C# 8.0的到来,它引入了接口默认实现,其目的正是允许在不需要更改现有实现的情况下向接口引入新方法。 - Olivier Jacot-Descombes
驱动这种变化的原因是您的类需要新/修改接口的功能。因此,您不需要寻找使用它的类。这些类将是更改的原因。它们将是起点。 - Scott Hannen
https://stackoverflow.com/users/880990/olivier-jacot-descombes - 我所做的难道不就是这样吗? - MLissCetrus
https://stackoverflow.com/users/5101046/scott-hannen - 有很多地方将接口传递到函数中并提取数据,但新功能不一定被使用...这些是我关心的接口更改。是否有必要更改它们? - MLissCetrus
你不将该方法添加到现有接口的原因是为了避免破坏性更改,对吗?更改现有接口的基本接口是一种破坏性更改。 - Mike Zboray
@mike-zboray - 是的 - MLissCetrus
1个回答

3
假设你的类依赖于 IWaiter:
public class FoodOrder
{
    private readonly IWaiter _waiter;

    public SmallRestaurant(IWaiter waiter)
    {
        _waiter = waiter;
    }

    // other methods, one of them needs the waiter.
}

...还有许多其他类似的类。这就是你的IWaiter

public interface IWaiter
{
    Bill GetBill();
}

出现这种情况是不应该的:你给 IWaiter 添加了一个新方法,然后你需要找到需要此新方法的类并更新它们以使用该新方法。原因有两个:

  • 这不是破坏性变更。如果其中一个类不需要新方法,则可以忽略它。
  • 流程应该反过来。您会将该方法添加到 IWaiter,因为类需要其服务员执行该新操作。需求始于依赖于IWaiter的类。他们是更改的原因,因此您不需要搜索它们。相反,添加一个其他类不需要的接口方法是没有意义的。

添加方法不是破坏性变更的事实应该消除创建新的“版本化”接口的需要,仅仅因为添加了一个新方法。

如果确实需要创建版本化接口,则上述规则同样适用。您将创建新接口是因为某些类需要它。您无需更新所有使用V1的类以使用V2。如果这样做,就没有必要保留V1。

版本控制有助于防止破坏性变更,如果我们发布了一些其他代码依赖的库,并且我们不想通过更改接口来破坏它们。如果所有内容都在我们自己的代码中,并且出于某种原因,我们必须对接口进行破坏性更改,则查找使用该接口的内容将非常容易。当我们更改接口时,编译器会在使用接口的任何位置引发错误。


谢谢...如果一个产品发布了(版本“n”),并且新功能被放入下一个版本(“n+1”)中,那该怎么办呢?旧客户仍将使用版本“n”,随着客户更新或新客户获得发布“n+1”,将使用功能 - 程序必须接受来自“n”或“n+1”的调用。 - MLissCetrus
没问题。如果你添加了一个新的方法,现有的客户端不必使用它。这不是一个破坏性的变化。问题是外部代码正在实现接口,现在这些类将不得不实现新的方法吗?如果是这样的话,那么你就不需要对接口进行版本控制。你只需要创建一个继承自旧接口的新接口。一个类可以更新以实现该接口,或者它可以继续实现旧接口。 - Scott Hannen

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