这些接口应该在接口级别继承还是在类级别实现?

6
我正在创建一个控制台应用程序,以学习状态模式和观察者模式。该程序基于基本订单系统,每个订单都可以有一个状态(待处理、准备好、已提交,即状态模式),并且对订单更新感兴趣的订阅者会收到通知(观察者模式)。
订单类实现了三个接口:IOrderIOrderStateIObservable,但我意识到我可以让这些接口相互继承。哪种方法更好?何时您会使类分别从每个接口实现,而不是像IOrder一样继承另外两个接口呢?
例如:
public class Order : IOrder, IOrderState, IObservable

或者

public interface IOrder : IOrderState, IObservable

编辑 我可能刚刚回答了自己的问题 - 如果您确定实现它的对象(Order类)将始终需要使用那些方法,您会使IOrder接口从其他两个接口继承吗?如果您不确定接口方法是否需要,或者其他类(例如,不需要被订阅者观察的订单)是否需要,那么您将在类级别单独包含接口?


你的 IOrder 接口没有那另外两个接口的话,它还有意义吗? - CSharpie
1
查看SOLID - Hamlet Hakobyan
@CSharpie,它只是定义了所有订单对象共有的方法和属性。但是我在想,如果我以后想要添加单元测试,我可以使用接口来创建模拟订单对象。出于这个原因,我通常会针对抽象编码。 - Theomax
@Hamlet Hakobyan,是的,我在考虑SOLID原则 - 请参见我的上面的评论。我想你应该总是依赖于抽象而不是具体类型,因此需要IOrder接口。还有接口隔离原则 - 所以我不应该让所有接口互相继承,因为可能会有某个类不需要实现所有接口成员?所以这个类应该单独实现它们? - Theomax
接口就是契约。您需要按照您的领域来思考。您需要哪些类型的契约以及哪些契约必须实现您的实体。 - Hamlet Hakobyan
4个回答

2
根据接口隔离原则 (Interface Segregation Principle),应该是IOrder接口实现IOrderState, IObservable接口。因此,无论哪个类实现了IOrder,它也会实现另外2个接口。

所以第二种情况更为合适。即:

public interface IOrder : IOrderState, IObservable

但是如果以后添加了不同的订单类型,并且不需要遵守观察者模式,那么就会被迫提供一个不需要的接口实现(IObservable)。 - Theomax
那么你的第一个建议更为合适 ;) - Wasif Hossain
1
因此,经验对于高效设计程序至关重要,有经验的架构师能够预见整个应用程序生命周期中可能发生的情况。然后,他尝试以这样的方式设计,以便代码可以轻松扩展和管理。 - Wasif Hossain
1
你最后的评论很有道理。谢谢。 - Theomax

1
设计类型时,您必须了解用例,或者至少考虑它们。创建接口时,应考虑其可能的实现方式。并非每个类都应该配备接口。 当您的库中每个类都是这样的时候:
interface IMyClass {}
class MyClass : IMyClass {}

如果出现这种情况,就需要再次审视类型设计。很可能你的类库会变得一团糟。我现在必须支持一些遗留代码,几乎每个内部类都有一个耦合的内部接口,而这个类只有一个实现。老实说,我想禁止我的前同事再使用接口了。
如果你能想象出任何其他的IOrder实现方式,那么就创建IOrder接口。否则,就放弃它(例如,我无法想象出两个或更多不同的IOrderState实现)。
如果每个IOrder都必须是IObservable,则从IObservable继承IOrder。这完全取决于你的用例。

1

根据您提供的具体用例信息有限,答案可能不是非常准确,但我会尽力给您一些指导和提示。

与其使用通用原则,我认为您需要考虑您的具体情况。

也许您可以问自己以下问题:

  • 所有IOrder消费者是否都需要观察订单?如果是这种情况,IOrder应该实现IObservable

  • 所有IOrder消费者是否都需要跟踪订单状态?如果是这种情况,IOrder应该实现IOrderState

实际上,我会询问这类问题以确定要做什么。

另一方面,您可以考虑更灵活的架构,在该架构中,应该实现Order的内容可以由通用约束定义:

public void Add<TOrder>(TOrder order) where TOrder : IOrder
{
}

但是当您需要跟踪订单状态时,可以这样做:
public void SaveState<TOrder>(TOrder order) where TOrder : IOrder, IOrderState
{
}

1
我认为这取决于接口的含义。尽管在您的示例中,将这些接口分开并没有太多意义,因为它们看起来非常相似。
然而,如果您能想到一个类只需要IOrder而不需要其他两个接口的情况,那么您可能需要将它们拆分。
让我们来看一下访问修饰符。如果您继承这些接口,所有内容都可能是公共可见的。
如果您仅希望Orderstate和Observation在内部使用,则必须将它们拆分。
例如,使用您的库的人应该只知道IOrder所公开的内容,而不是IOrderstate。

单独的接口用于实现状态模式和观察者模式,并保持代码组织的清晰。也许这并不重要,也许只是因为系统应该如何工作而人们总会有自己的实现变体? - Theomax
1
我还创建了这些单独的接口,以允许为单元测试创建模拟对象。如果这些方法是在一个接口中定义,我想它们可能会被存根或仍然被模拟。 - Theomax

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