Reputation: 85561
As I mention in an earlier question, I'm refactoring a project I'm working on. Right now, everything depends on everything else. Everything is separated into namespaces I created early on, but I don't think my method of separtion was very good. I'm trying to eliminate cases where an object depends on another object in a different namespace that depends on the other object.
The way I'm doing this, is by partitioning my project (a game) into a few assemblies:
GameName.Engine
GameName.Rules
GameName.Content
GameName.Gui
The GameName.Engine
assembly contains a bunch of interfaces, so other parts of the program don't need to depend on any particular implementation. For instance, I have a GameName.Engine.ICarryable
interface that is primarily implemented by GameName.Content.Item
class (and its sub-classes). I also have an object to allow an Actor
to pick up an ICarryable
: PickupAction
. Previously
, it required an Item, but this exposes unneccessary methods and properties, where it really only needed the methods required to pick it up and carry it. That's why I've created the ICarryable
interface.
Now that's all good, so to my question. GameName.Gui
should only depend on GameName.Engine
, not any implementation. Inside GameName.Gui
I have a MapView
object that displays a Map
and any IRenderable
objects on it.
IRenderable
is basically just an interface that exposes an image and some strings describing the object. But, the MapView also needs the object to implement ILocateable
, so it can see its location and know when it's changed via an event, LocationChanged
, inside ILocateable
.
These two interfaces are implemented by both Item
and Actor
objects. Which, again are defined in GameName.Content
. Since it needs both interfaces, I have two choices:
Make GameName.Gui
depend on GameName.Content
and require an Entity
(base-class of Item
and Actor
).
Make an interface inside GameName.Engine
that looks like this:
interface ILocateableRenderable : ILocateable, IRenderable
{
}
And then make my Actor
and Item
objects implement that interface instead of the two individually.
Anyone have any suggestions on which method is the best? Is it appropriate to create an interface with no methods or properties, that only enforces implementing two other interfaces?
Clarification: MapView
works on a Map
, which is composed of Entity
objects. I don't want to expose the Entity
objects to the MapView
, it only needs to know their location (ILocateable
) and how to render them (IRenderable
).
Upvotes: 0
Views: 7802
Reputation: 85561
In all cases, the IRenderable objects would also have to implement ILocateable, so I'm doing what @Sklivvz suggested and make IRenderable implement ILocateable:
public interface IRenderable : ILocateable
{
// IRenderable interface
}
This pattern is used heavily in .NET. For example, the ICollection interface implements IEnumerable, while adding additional methods. This is a direct analogy to what the above code would do.
Thanks @Sklivvz.
Upvotes: 2
Reputation: 136613
Option 2 violates the Interface Segregation Principle. Basically a client should not have to see methods that it doesn't need... no fat interfaces. If an object cannot just implement any one of those Interfaces, then you should combine them to be a single interface. However if it makes sense to have an object just be ILocatable without it having to be a IRenderable, keep the interfaces distinct. I don't see a problem with that.
You can create a default base class (implementing both) to derive from in case you find that to be the common/80% scenario
Upvotes: 0
Reputation: 31133
You seem to have two conflicting requirements:
Inside GameName.Gui I have a MapView object that displays a Map and any IRenderable objects on it.
and
But, the MapView also needs the object to implement ILocateable, so it can see its location and know when its changed via an event, LocationChanged, inside ILocateable.
So, if the MapView only needs IRenderable, then it should accept IRenderable and then check whether the class also implements ILocateable. In this case use its methods.
public void Whatever(IRenderable renderable)
{
if (renderable is ILocateable)
{
((ILocateable) renderable).LocationChanged += myEventHandler;
}
// Do normal stuff
}
On the other hand, if you always need it to be ILocateable and IRenderable, then you should really create a derived interface in one of two ways Either
interface IMappable: IRenderable, ILocateable {}
public void Whatever(IMappable mappable)
{
mappable.LocationChanged += myEventHandler;
// Do normal stuff
}
or
interface IRenderable: ILocateable
{
// IRenderable interface
}
public void Whatever(IRenderable renderable)
{
renderable.LocationChanged += myEventHandler;
// Do normal stuff
}
depending on how your code is at the moment.
Upvotes: 3
Reputation: 42036
What you might consider, but I guess I am no expert in the area, is to split the engine in two, a renderer and a location-manager. You could then add ILocateable objects to the latter and IRenderable ones to the former. However if you think this doesn't make any sense and/or is far too complex, creating this merged interface doesn't seem too bad. Another option might be to add a super-interface to both ILocateable and IRenderable, for example IGameObject, that you would accept in your engine class, and then check the exact type to dispatch objects to the map, the location-thing or both.
Upvotes: 0