【问题标题】:Combining two interfaces into one将两个接口合二为一
【发布时间】:2008-10-16 07:54:51
【问题描述】:

正如我在之前的问题中提到的,我正在重构我正在处理的项目。现在,一切都取决于其他一切。一切都被分离到我早期创建的命名空间中,但我认为我的分离方法不是很好。我试图消除一个对象依赖于不同命名空间中的另一个对象的情况,该命名空间又依赖于另一个对象。

我这样做的方式是将我的项目(游戏)划分为几个程序集:

GameName.Engine
GameName.Rules
GameName.Content
GameName.Gui

GameName.Engine 程序集包含一堆接口,因此程序的其他部分不需要依赖任何特定的实现。例如,我有一个GameName.Engine.ICarryable 接口,它主要由GameName.Content.Item 类(及其子类)实现。我还有一个对象可以让Actor 接收ICarryablePickupActionPreviously,它需要一个 Item,但这暴露了不必要的方法和属性,它实际上只需要拾取并携带它所需的方法。这就是我创建ICarryable 接口的原因。

现在这一切都很好,所以我的问题。 GameName.Gui 应该只依赖于GameName.Engine,而不是任何实现。在GameName.Gui 内部,我有一个MapView 对象,它显示Map 和上面的任何IRenderable 对象。

IRenderable 基本上只是一个公开图像和一些描述对象的字符串的接口。但是,MapView 还需要对象实现ILocateable,因此它可以通过ILocateable 内部的事件LocationChanged 看到它的位置并知道它何时被更改。

这两个接口由ItemActor 对象实现。其中,再次在GameName.Content 中定义。由于它需要两个接口,我有两个选择:

  1. 使 GameName.Gui 依赖于 GameName.Content 并需要 EntityItemActor 的基类)。

  2. GameName.Engine 中创建一个如下所示的接口:

    interface ILocateableRenderable : ILocateable, IRenderable
    {
    }
    

    然后让我的ActorItem 对象实现该接口,而不是单独实现这两个。

有人对哪种方法最好有任何建议吗?创建一个没有方法或属性的接口是否合适,只强制实现另外两个接口?

澄清:MapView 作用于 Map,它由 Entity 对象组成。我不想将Entity 对象暴露给MapView,它只需要知道它们的位置(ILocateable)以及如何渲染它们(IRenderable)。

【问题讨论】:

    标签: c# .net oop


    【解决方案1】:

    您似乎有两个相互矛盾的要求:

    在 GameName.Gui I 中 有一个 MapView 对象,该对象显示 映射及其上的任何 IRenderable 对象。

    但是,MapView 也需要对象 来实现 ILocateable,所以它可以 查看它的位置并知道它的时间 通过事件 LocationChanged 更改, 在 ILocateable 中。

    所以,如果 MapView 只需要 IRenderable,那么它应该接受 IRenderable,然后检查该类是否也实现了 ILocateable。在这种情况下使用它的方法。

    public void Whatever(IRenderable renderable)
    {
       if (renderable is ILocateable)
       {
          ((ILocateable) renderable).LocationChanged += myEventHandler;
       }
    
       // Do normal stuff
    }
    

    另一方面,如果您总是需要它是 ILocateable 和 IRenderable,那么您应该以两种方式之一创建派生接口 要么

    interface IMappable: IRenderable, ILocateable {}
    
    public void Whatever(IMappable mappable)
    {
       mappable.LocationChanged += myEventHandler;
    
       // Do normal stuff
    }    
    

    interface IRenderable: ILocateable
    {
      // IRenderable interface
    }
    
    
    public void Whatever(IRenderable renderable)
    {
       renderable.LocationChanged += myEventHandler;
    
       // Do normal stuff
    }  
    

    取决于您的代码目前的情况。

    【讨论】:

    • 嗯,我没想过要让 IRenderable 实现 ILocateable。考虑一下,它不会因为有一个不是 ILocateable 的 IRenderable,因为如果 MapView 不知道它在哪里,它肯定无法渲染它。
    【解决方案2】:

    在所有情况下,IRenderable 对象也必须实现 ILocateable,所以我按照@Sklivvz 的建议做,让 IRenderable 实现 ILocateable:

    public interface IRenderable : ILocateable
    {
      // IRenderable interface
    }
    

    这种模式在 .NET 中被大量使用。例如,ICollection 接口实现了 IEnumerable,同时添加了其他方法。这与上述代码的作用直接相似。

    感谢@Sklivvz。

    【讨论】:

      【解决方案3】:

      您可能会考虑,但我想我不是该领域的专家,将引擎分成两部分,一个渲染器和一个位置管理器。然后,您可以将 ILocateable 对象添加到后者,并将 IRenderable 对象添加到前者。但是,如果您认为这没有任何意义和/或过于复杂,那么创建此合并界面似乎还不错。另一种选择可能是向 ILocateable 和 IRenderable 添加一个超级接口,例如 IGameObject,您将在引擎类中接受该接口,然后检查确切的类型以将对象分派到地图、位置对象或两者。

      【讨论】:

        【解决方案4】:

        选项 2 违反了Interface Segregation Principle。基本上,客户端不应该看到它不需要的方法......没有胖接口。如果一个对象不能只实现这些接口中的任何一个,那么您应该将它们组合成一个接口。但是,如果让一个对象只是 ILocatable 而不必是 IRenderable 是有意义的,那么请保持接口不同。我认为这没有问题。

        您可以创建一个默认基类(同时实现两者)来派生,以防您发现这是常见/80% 的场景

        【讨论】:

        • 告诉 System.Windows.Forms.Control! ;-)
        猜你喜欢
        • 2015-12-30
        • 2019-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-17
        • 2015-10-24
        • 2010-12-28
        • 2017-06-23
        相关资源
        最近更新 更多