I've read a few articles about the Entity-Component style of programming. One of the common problems posed is how to express dependencies between components, and how components related to the same entity communicate.
It seems to me that a simple solution to this problem is to make each dependency a virtual base class of its dependant.
That way, when a component is included in an entity (via virtual inheritance), all of the dependant components are included exactly once. Additionally, all of the functionality that a component depends upon will be available in its member functions.
class C_RigidBody : public virtual C_Transform {
public void tick(float dt);
};
class C_Explodes : public virtual C_Transform {
public void explode();
};
class E_Grenade : public virtual C_RigidBody, public virtual C_Explodes {
//no members
};
Is there any reason no one does this?
(I realize that multiple inheritance is usually frowned upon due to the "diamond problem," but this problem is something components have to deal with anyway. (Imagine how many components would depend on an entity's position in the game world))
I recently come up with the same idea with you too.
In theory I blieve that this approach is a perfect solution for Inverse of Dependency if properly applied. Otherwise you'll mess things up.
Recalling what DIP states:
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.
See the following code example
struct IClockService
{
virtual unsigned timestamp() = 0;
};
struct ITimingService
{
virtual unsigned timing(std::function<void()> f) = 0;
};
struct ClockService : public virtual IClockService // public virtual means implement
{
virtual unsigned timestamp() { return std::time(nullptr); }
};
struct TimingService : public virtual ITimingService
, private virtual IClockService // private virtual means dependency
{
virtual unsigned timing(std::function<void()> f)
{
auto begin = timestamp();
f();
return timestamp() - begin;
}
};
class Application : public ClockService
, public TimingService
{
};
Application a;
unsigned runingTime = a.timing(anyFunction);
In the above example. ClockService
and TiminigService
are two module, they don't need to know each other but only the interface IClockService
and ITimingService
. And the Application
composits the two modules together.
In conclusion:
I do only apply such idioms in my toy projects. Take it at your own risk.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With