每个界面一个方法是合理的设计选择吗?

本文关键字:选择 界面 方法 一个 | 更新日期: 2023-09-27 18:06:12

使用IAddable, imovable, IIndexable这样的接口来支持每个操作是否有意义,或者这种方法最终会导致无法管理的代码?

每个界面一个方法是合理的设计选择吗?

假设您想实现一种类似于数字集合的NumberBucket

可以创建一个有点胖的界面,像这样:

public interface INumberBucket
{
    void Add(int number);
    void Remove(int number);
}

你可以毫无问题地实现它:

 public class NumberBucket : INumberBucket
{
    public void Add(int number)
    {
        Console.WriteLine($"Adding {number}");
    }
    public void Remove(int number)
    {
        Console.WriteLine($"Removing {number}");
    }
}

但是后来你决定你需要实现一个ReadonlyNumberBucket,其中数字不能被删除:

public class ReadonlyNumberBucket : INumberBucket
{
    public void Add(int number)
    {
        Console.WriteLine($"Adding {number}");
    }
    public void Remove(int number)
    {
        throw new NotImplementedException();
    }
}

好吧,现在没有Remove的逻辑实现,所以你必须把它变成no-op或让它抛出。这违反了Liskov替换原则

如果你声明了两个聚焦的接口:IAddableIRemovable,你就可以不实现IRemovable。这是接口隔离原则的基础。

所以,回答你的问题:是的,是合理的,但是在你看到投资回报之前可能需要一段时间。就像@PhilipStuyck在评论中写的那样:

[. .问题是这个例子是否适用于你的情况。这个答案中的第一个界面可能对你来说已经足够好了,把它分开可能是过度设计。过度设计也是一种代码气味。你得知道什么时候应用哪种模式。根据问题中的简短解释,我不知道。

这取决于应用程序设计中"控件"的级别。如果你的道具是"可添加的",但不是"可分割的",那么你所说的就很有意义了。

但是如果在你的应用领域中,所有项目通常都支持你所描述的基本操作,那么就没有理由把它们移到单独的界面中。

我看到一些评论,认为如果让所有方法都在一个接口内,Liskov替换可能会被破坏。说实话,即使是微软有时也会违反Liskov替代原则。例如,即使Array类型不支持此接口上的某些方法,他们也让Array实现IList(例如,如果将数组强制转换为IList,"Add"方法会抛出NotSupportedException类型的异常)。

最重要的是:你看到你的方法之间的任何逻辑关系吗?至少他们必须属于某个类别或什么的。如果是,将它们组合到一个接口中。在你的例子中,IAddable和imovable在我看来是相互关联的,因为它们都代表着相似的东西:一个对象可以从某个篮子中移动(添加或删除)。对我来说,IIndexable似乎是一种"奖励",就像你可以添加但不是强制性的东西。所以,如果我是你的架构师,我会把IIndexable方法放在一个单独的接口中。

无论如何,如果在遥远的将来,一个对象是"可添加的",但不支持"删除",那么你可以很容易地解决这个问题,把你的接口分成两个,让我们的旧接口继承它们:IAddable, IRemovable。我倾向于在适当的时候"隔离"接口,我不喜欢从架构的一开始就分离接口。

所以,记住:

  • 一开始,只隔离明显的东西,然后在需要隔离的情况下隔离,甚至你可以选择为了可读性/框架理解而不隔离(就像微软那样,因为对他们来说,数组是一种列表,这应该保持原样)。

  • 像SOLID这样的模式和原则只是一个非常强大的指导,但规则有时是用来打破的。