I have this out of context scenario, where what I think is good practices leaves me in a situation of both implementing an interface, and using composition to do the implementation.
Imagine the following:
I have a Character
with Health
and Mana
, like so:
interface ICharacter
{
Health Health { get; }
Mana Mana { get; }
}
class Character : ICharacter
{
public Health Health { get; private set; }
public Mana Mana { get; private set; }
public Character(Health health, Mana mana)
{
this.Health = health;
this.Mana = mana;
}
}
I then decide, that Health
and Mana
should be combined into one object, since they belong together.
interface IResource
{
Health Health { get; }
Mana Mana { get; }
}
I change my Character
to
interface ICharacter
{
IResource Resource { get; }
}
class Character : ICharacter
{
public IResource Resource { get; private set; }
public Character(IResource resource)
{
this.Resource = resource;
}
}
However, so that clients of Character
can access a character's health without saying character.Health.Current
and "violating" the principle of least knowledge I want Character
to provide this information. And to improve encapsulation I would no longer disclose how Character
stores its Health
and Mana
, like so:
interface ICharacter : IResource
{
}
class Character : ICharacter
{
private IResource resource;
public Health Health
{
get
{
return resource.Health;
}
}
public Mana Mana
{
get
{
return resource.Mana;
}
}
public Character(IResource resource)
{
this.resource = resource;
}
}
This gets my Character
back to having Health
and Mana
which I like, but Character
is now implementing the same interface it is am composed of.
In implementation, this looks quite a lot like the Decorator Pattern, but in intention, they have nothing in common.
Questions
-
Is this pattern / antipattern of implementing the same interface that a class is composed of recognized, and if so, what is it called?
-
Would you consider this good a idea over just having a property to IResource? – Why? / why not?
-
Any situations to be wary off, where this is a definitive no-go?
Sidenote – ICharacter
would certainly have more methods, but to shorten it and keeping it concise, I only included the relevant part here.
Best Answer
The principle is correctly quoted as "prefer composition over inheritance". No one said you couldn't do both. Sometimes you have to do both. But given a choice, composition is usually the better one.
Your
IResource
reminds me of different patterns than the ones you mention. Parameter object makes constructing an object easier by bundling it's dependencies together.Beware, taken to an extreme this smacks of a service locator which can be bad in some situations.
Respecting the Law of Demeter is a good thing if you understand it well. It is causing you a problem here but it's a good problem. It's forcing you to simplify access to health and mana until it's obvious that your
Character
isn't doing anything but aggregation.What's missing is abstraction. Sure you promised
Character
would have more methods but as far as I see hereCharacter
isn't adding anything but unneeded indirection.Right now
Character
looks like a brain dead value object (such as String). If that's what it's going to be then give it it's own copies of these values and flatten it out.If
Character
is going to be a behavior object I'd expectHealth
to be hidden by encapsulation and interacted with using methods likeWound(int)
andDie()
notGetHealth()
. This follows Tell, don't ask. A character's heath status is no one else's business. This is real encapsulation.In either case the
Character
class should not be a brief stop on the way to dealing with character details. This should be THE place to resolve them. Don't make me dig past it.