WorkingThe interface segregation principle is focused more on deleting my postscompletely irrelevant functionality. Here it's not so clear cut.
A blatant example of a violation of ISP is a Scrollbar
interface with positive answersa setText
function inherited from Widget
, when a scrollbar won't even show any text. GoingIn such a case, the function may even be documented as irrelevant to callers, and to attempt to call such a function on a scrollbar may indicate a logical error.
Such blatant violations of ISP tend to be most foul, with monolithic interfaces that might have 200 functions with only 20 of them being relevant for the actual object in question. I see it often in GUI designs which are based around massive inheritance hierarchies, and often with documentation mechanisms trying to filter out the enormous amount of irrelevant derived functionality.
In your case, I think it's a bit different since given a function like this:
virtual bool handleInput(KeyEvent aKeyEvent, ...) = 0;
... it's still relevant to clients whether or not a view handles keyboard input. The fact that it doesn't is still relevant information, and worth calling the function to find out. So I don't see this as a clear violation of ISP.
You could try to split up the interface into multiple interfaces like IKeyboardHandler
, but it's likely to make things harder rather than easier. It's worth noting the scope here in terms of dependencies.
A game will typically only have a handful of such views. You might have a main menu screen of sorts, splash screen, HUD, game view, maybe an in-game pie menu or something. It's a handful of views. The clients will also tend to be few in number, maybe just one broad client which is the one handling events from the operating system and calling the relevant view functions. So it's very limited in scope/scale when it comes to the number of dependencies, and that's a sign that you don't need to try too hard to come up with a great engineering solution, and that doing so may very well contradict your goals for maximum negative votes!ease of maintenance (ease of change).
Avoiding the Dynamic Cast
That said, just for completeness, I'll show you a simple way to avoid the cast in these kinds of scenarios. You can do this:
class IView{
public:
...
// Returns null if keyboard handling is unsupported.
virtual IKeyboardHandler* keyboard() = 0;
// Returns null if mouse handling is unsupported.
virtual IMouseHandler* mouse() = 0;
};
... view types that don't need inherit from (implement) IKeyboardHandler
can simply return null, e.g. Ones that do can simply return this;
. This way you can find out if a view supports keyboard handling with an interface query which doesn't involve casting, and if so, handle keyboard input for that view.
Taking this technique further to a greater degree of flexibility will often have you approaching COM-style designs (which does involve casting but hides it from the client), and even further, entity-component systems (which can be very useful to know about as a game developer, but probably outside of this view context). Yet I think this is absolute overkill for your case even with the basic form, and likely to just add more burdens upfront without alleviating any later.
Yet this basic version shown above can be a useful design technique if you had, say, functions in your system which can work strictly with IKeyboardHandler
, and you potentially have things other than views which might implement this keyboard handling interface.