每个接口一个方法是合理的设计选择吗?

Would one Method per Interface be a Reasonable Design Choice?

使用 IAddable、IRemovable、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 or make it throw. This violates the Liskov Substitution Principle

如果你贴上了两个 focused 接口:IAddableIRemovable 你可能不会实现 IRemovable。这是 Interface Segregation Principle.

的基础

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

[..] The question is if this example is applicable in your situation. The very first interface in this answer might be good enough for you and splitting it might be overdesign. Overdesign is also a code smell. You gotta know when to apply which pattern. Based on the short explanation in the question I cannot tell.

这取决于您在应用程序设计中的 "control" 级别。如果您有可能 "Addable" 而不是 "Devidable" 的项目,您说的很有道理。

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

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

最重要的是:您是否看到您的方法之间有任何逻辑关系?至少他们必须属于某个类别或某物。如果是这样,将它们组合到一个界面中。在您的情况下, IAddable 和 IRemovable 对我来说似乎以某种方式相关,因为它们都代表类似的东西:可以从某个篮子中移动(添加或删除)一个对象。对我来说,IIndexable 看起来像一个 "bonus",你可以添加但不是强制性的。所以如果我是你的架构师,我会把 IIndexable 方法放在一个单独的接口中。

无论如何,如果在遥远的将来某个对象会是 "addable" 但不支持 "remove" 那么您可以通过将接口一分为二并让我们的旧接口继承它们来轻松解决这个问题两者:IAddable、IRemovable。我倾向于在适当的时候 "segregate" 接口,我不喜欢从我的架构一开始就拆分我的接口。

所以,记住:

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

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