正如我在之前的一个问题中提到的那样,我正在重构一个我正在研究的项目。现在,一切都取决于其他一切。一切都被分成了我早期创建的名称空间,但我不认为我的分离方法非常好。我试图消除一个对象依赖于另一个对象的情况,这个对象依赖于另一个对象。
我这样做的方法是将我的项目(游戏)划分为几个程序集:
GameName.Engine
GameName.Rules
GameName.Content
GameName.Gui
GameName.Engine
程序集包含许多接口,因此程序的其他部分不需要依赖任何特定的实现。例如,我有一个GameName.Engine.ICarryable
接口,主要由GameName.Content.Item
类(及其子类)实现。我还有一个对象允许Actor
选择ICarryable
:PickupAction
。 Previously
,它需要一个Item,但这暴露了不必要的方法和属性,它实际上只需要拾取和携带它所需的方法。这就是我创建ICarryable
界面的原因。
现在这一切都很好,所以我的问题。 GameName.Gui
应仅依赖于GameName.Engine
,而不是任何实施。在GameName.Gui
内,我有一个MapView
对象,在其上显示Map
和任何IRenderable
个对象。
IRenderable
基本上只是一个暴露图像和描述对象的字符串的接口。但是,MapView还需要该对象来实现ILocateable
,因此它可以通过LocationChanged
内的事件ILocateable
查看其位置并知道它何时发生了变化。
这两个接口由Item
和Actor
对象实现。其中,再次在GameName.Content
中定义。由于它需要两个接口,我有两个选择:
让GameName.Gui
取决于GameName.Content
,并且需要Entity
(基类Item
和Actor
)。
在GameName.Engine
内创建一个如下所示的界面:
interface ILocateableRenderable : ILocateable, IRenderable
{
}
然后让我的Actor
和Item
个对象实现该界面而不是单独的两个。
任何人对哪种方法最好有任何建议?是否适合创建没有方法或属性的接口,只强制实现其他两个接口?
澄清:MapView
适用于Map
,它由Entity
个对象组成。我不想将Entity
个对象公开给MapView
,只需知道它们的位置(ILocateable
)以及如何渲染它们(IRenderable
)。< / em>的
答案 0 :(得分:2)
您似乎有两个相互矛盾的要求:
Inside GameName.Gui I 有一个显示的MapView对象 a 在其上映射和任何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
}
取决于您的代码目前的情况。
答案 1 :(得分:2)
在所有情况下,IRenderable对象也必须实现ILocateable,所以我正在做@Sklivvz建议并从ILocateable进行IRenderable继承(这是它在讨论接口时所称的那个?):
public interface IRenderable : ILocateable
{
// IRenderable interface
}
这种模式在.NET中使用很多。例如,ICollection接口实现IEnumerable,同时添加其他方法。这与上述代码的作用直接类比。
谢谢@Sklivvz。
答案 2 :(得分:0)
您可能会考虑什么,但我想我不是该领域的专家,是将引擎分成两部分,一个渲染器和一个位置管理器。然后,您可以将ILocateable对象添加到后者,将IRenderable对象添加到前者。但是,如果您认为这没有任何意义和/或太复杂,那么创建这个合并的界面似乎并不太糟糕。另一种选择可能是在您的引擎类中接受ILocateable和IRenderable(例如IGameObject)的超级接口,然后检查确切的类型以将对象分派到地图,位置或两者。 / p>
答案 3 :(得分:0)
选项2违反了Interface Segregation Principle。基本上,客户端不应该看到它不需要的方法......没有胖接口。如果一个对象不能只实现这些接口中的任何一个,那么你应该将它们组合成一个单独的接口。但是,如果让对象只是ILocatable而不必是IRenderable是有意义的,那么保持接口不同。我没有看到这个问题。
您可以创建一个默认基类(实现两者),以便在您发现它是常见/ 80%场景的情况下派生