On 13.07.2017 15:12, bitrex wrote:
> Hi, I'm still working on my own little "game engine" as a C++ hobby
> project. I'm wondering about some general OOP design issues and hope
> maybe someone can give a bit of advice on a sound design principle for
> the following situation.
>
> I have an abstract pure-virtual class called something like
> DisplayObject, which currently can be further subclassed into child
> classes like "DisplayObject2D", "DisplayObject3D", etc.
>
> The interface is fairly simple, for example there's a method which
> returns a std::vector of std::weak_ptrs to the sprites for the rendering
> thread to hang onto in a vector, and a "render" method to be overridden
> for the rendering thread to call.
>
> There's a handle class called say "GameObject" which composites various
> DisplayObjects and will also composite the classes which the logic code
> operates on.
>
> So I'm also using some CPU-based visual effects which inherit from
> AbstractDisplayObject's interface, but this has lead me to three levels
> of inheritance; say AbstractDisplayObject -> EffectObject ->
> ConcreteEffectObject where EffectObject also has some virtual methods
> for _its_ child classes to override and...this is getting hairy and sort
> of brittle already.
What's wrong with many levels of inheritance? If it suits the task then
there should be no problem whether there is 3 or 30 levels of
inheritance. However, if the inheritance does not suit the task (e.g.
Liskov principle does not hold) then even 1 level is too much.
If the things are getting too hairy/brittle to maintain, then automatic
tests will save your day! Write automatic unit/integration tests
covering all of the functionality which the app must have, then add new
functionality while keeping the tests passing, then refactor the code to
become simpler while keeping the tests passing. Rinse and repeat as
often as needed. I have lost the grasp of all aspects in our software a
long time ago; the unit test suite is the only place this knowledge
still exists I'm afraid.
Last, but not least: each overridden virtual function must be declared
as 'override'! Otherwise refactoring becomes way too brittle.