Interfaces and code duplication

Started by
9 comments, last by WoopsASword 7 years, 8 months ago

To start, my background is C++, Just tinkering really; a few simple 3D games but nothing professional. Anyway.

I'm starting developing for my Android phone, which realistically means learning Java. Its been pretty easy so far as the two languages are similar.

However, I'm getting a little lost on the use of Interfaces. Every Java resource I've read goes crazy on the idea of interfaces, and uses them for almost everything. What I don't get however, is how they are better than plain old inheritance. The only thing they seem to be able to do is allow (a form of) multiple inheritance, which you're not allowed to do in Java by just extending classes, and in the cases I've been sketching out in readiness for coding, it appears to mean a lot of redundant code.

Say I have a class GameObject, being the abstract base class for everything in my game. Something in the game might be a visible physics object, so if I want an object that does that using Interfaces, I would write a Renderable interface and a Physics interface and implement both.

But say I have an object which doesn't use physics. I would have it implement just the Renderable interface.

Now I have my two objects which I can store in a collection and pass to a Renderer, the interfaces guaranteeing that both objects have the appropriate methods to be able to be drawn.

However, because Interfaces only contain the method declarations, not any actual functionality itself, in this case I'd have to write the drawing methods TWICE, for both the renderable physics object and the plain non-physics object.

In C++, I'd just have them inherit from a Renderable and a Physics base class to give them all the functionality they need, but in Java I can't do that as you can only inherit from one class.

Given this restriction, currently, I'm looking at just having a Renderable class extend the Base class, and the Physics object extend the Renderable without using Interfaces at all. But given how much they're pushed in the various coding resources, I can't help but think I'm missing something.

Why are Interfaces used instead of just Inheritance alone?

Advertisement
You're thinking in terms of inheritance when you should really be thinking in terms of composition. That means you really want to avoid thinking in terms of "is a" and more in terms of "has a".

Try it. GameObject is a Renderable. GameObject has a Renderable.

The first one makes you think of inheritance. The second one leads you to think of composition.

In this case, GameObject can just contain a member variable that points to a Renderable object. It's not a base of GameObject but rather it's contained by GameObject. This lets you have as many of these items as you want.

Note that this isn't a Java-only issue. In C++, your proposed solution of just inheriting from both Renderable and Physics has a _lot_ of potential problems. Name conflicts, up casts problem, performance (via memory or pointer shenanigans), etc.

Think in terms of composition. As a bonus, think on it hard enough and you'll eventually discover _dynamic composition_, which is how a vast majority of games/engines are structured these days: a GameObject essentially just becomes a bag of independent Components that implement pieces of functionality (or contain chunks of inert data that inform other modules which functionality the GameObject should have).

The big advantage there is that you don't have to make a big nasty class hierarchy to make individual objects and instead can make everything data-driven and composable.

Obligatory reading: Evolve Your Hierarchy

If you want to buy into the ECS variant of all this (e.g. where your components are just inert data) there's also the classic Artemis library for Java that got popularized around the same time as previous article. There are many alternatives for Java these days, as well as similar framework for C++, C#, JavaScript, you name it. You can also code up much simpler component-based engines that are arguably superior solutions for real-world projects. :)

Sean Middleditch – Game Systems Engineer – Join my team!

Every Java resource I've read goes crazy on the idea of interfaces,
Then those "resources" are ugly and stupid. And if they preffix their interfaces with "I" ? Even more stupid.

Interfaces are nice when you're doing "dependency inversion". Ie, instead of manipulating an object and adding your code to make it do some work, the object defines a common interface it needs, you pass something that implements it, and the object does its job with it.

For example, when you're designing some class that needs some extra object to work, but you dont want to limit the inheritance tree of that object, and also the object needs a well defined interface to work with. You could do two things, either the object exposes some methods that produce intermediary results that you, the user of the object, has to grab and manage to get a result, or the object could say "Hey, I need a class that implements x, y and z methods! Hand it to me and I'll do what I have to do!". Second is more robust.

To be honest, I haven't seen many use cases of classic interfaces in game programming. But game programming isnt all the programming there is, they're particulary popular in web development (where most Java development is done, thus why Java resources gravitate to the aspects you mentioned), where you have tons of different frameworks, layers and systems that operate with eachother, while at the same time tackling very similar issues. For example, persistence: Something like the JPA specification defines interfaces that have a particular behavior, and the developers who make libraries that implement JPA (Hibernate, EclipseLink, etc), implement those interfaces and try to follow that behavior. So in theory, the user can get more easily started with either framework if they use JPA standard interfaces, and can go deeper by using specific classes of each library if they need to. Establishing a common ground among persistence libraries.

Interfaces are popular with dependency injection for example, where you inject and switch at runtime concrete dependencies that implement some common interface that the client object needs to work. You can Google around for learning that kind of stuff, although its not quite related to game programming.

On your concrete issue, I dont think you need a hierarchy of renderables. Usually when you start splitting concerns a bit it becomes more clear that you can abstract rendering in a few render task classes, possibly just only one very data driven task class, and not much else beyond a renderer that can grab those tasks and draw them as they specify they should be drawn.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

If you want to buy into the ECS variant of all this (e.g. where your components are just inert data) there's also the classic Artemis library for Java that got popularized around the same time as previous article.

That site went offline a while ago, I dont think whoever picked it up is actually the original Artemis developer. Just look at the other posts in there.

This is what I'd use: https://github.com/junkdog/artemis-odb Its a fork of Artemis, much further developed. Original Artemis had a bunch of bugs and issues, was pretty much abandoned after a while. It spawned tons of forks though. I have my own (in my signature!), but artemis-odb is probably the most developed and widely used right now (and they support Android).

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

Thanks for the advice and the quick replies :)

Riiiiightt..... I think I'm getting there. The concepts are still floating in my head just out of reach, but they're beginning to brush against my fingertips.

So, I could make a class called Renderable, which contains all the basic things such as position, graphics (or location of graphics at least). To make an object Renderable, I'd just add a pointer to an instance of the Renderable class, rather than directly inheriting from it.

My Renderer would need to take an object of type 'Renderer' - I think I see now where Interfaces fit in. I could make my object implement a 'Renderer' interface (but I'll not call it IRenderer, for fear of angering TheChubu... :wink: ) and implement a method to direct it to the renderer class. True, I'd need to duplicate those couple of lines but the actual heavy-lifting would be handled by the Renderer component.

Better?

Obligatory reading: Evolve Your Hierarchy

Excellent read, thank you.

So, I could make a class called Renderable, which contains all the basic things such as position, graphics (or location of graphics at least). To make an object Renderable, I'd just add a pointer to an instance of the Renderable class, rather than directly inheriting from it.

My Renderer would need to take an object of type 'Renderer' - I think I see now where Interfaces fit in. I could make my object implement a 'Renderer' interface (but I'll not call it IRenderer, for fear of angering TheChubu... :wink: ) and implement a method to direct it to the renderer class. True, I'd need to duplicate those couple of lines but the actual heavy-lifting would be handled by the Renderer component.

How about: GameObject has a Renderer, Renderer is an abstract, so you can have any kind of concrete renderers attached to the GameObject. No interface needed. Unless Renderer can be anything else than Renderer itself that you decide to have it as interface ... which doesn't make any sense.

I just believe using interface is something that is highly general, like #init #update #shutdown kind of methods or the ones for finite state machine, something where you have to force any kind of classes to be like the interface (which should not be specific like Renderer).

As this is For Beginners, trying to stick with answering your actual questions before the educational aspects...

Every Java resource I've read goes crazy on the idea of interfaces, and uses them for almost everything. What I don't get however, is how they are better than plain old inheritance.

Because as far as software architecture goes, it is a much better design in general. There are some specific cases where that isn't true, but in the general case you should depend on abstractions rather than concrete types.

The are better than "plain old inheritance" because depending on an abstraction means you can add whatever implementation details you want later and they automatically fit into the system, they just work.

An example I frequently give is with graphics systems.

Programmers rely heavily on the abstract classes in Direct3D yet they don't care one bit about the concrete types.

I request an ID3D11Device* and it just works. I can do all the things I want to with that device. I don't care if the device driver actually implements it as an ID3D11GeForce620, or an ID3D11Radeon6230, or an ID3D11IntelHD4400. The only thing I care about is that it does all the things an ID3D11Device is supposed to do.

I can use my object, no matter what the final concrete type happens to be, and it should work perfectly. Every concrete type is completely replaceable with any other concrete type. It does not matter which one the system provides. If I obtain something that implements an interface it implements it perfectly and could have been interchanged with any other object that implements the type. That doesn't mean they perform identically, different graphics cards from different eras may perform faster or slower, or may perform different operations at different rates, but (barring driver bugs) they will always implement every operation exactly as specified, and you can do all the operations on any card that follows the interface.

I shouldn't ever need to write code that detects what specific device it happens to be and write special-case code for that concrete type; instead I can use the provided interface and decide based on that. Some functionality may differ between cards but I don't need to hard-code every single card that can support the feature, I can rely on the interface to query if the feature exists by getting device capabilities.

That last point also allows future-proofing. If I write my program that works on all of today's cards, and sometime in the future someone introduces a new card with different capabilities, it just needs to implement the right interface and it drops in perfectly. If it provides different capabilities they must be queried exactly the same way the interface allows for querying capabilities.

I don't need to modify today's program to support a new graphics card that ships three years from now, it will still work perfectly because it follows the same interface and is completely interchangeable with any other graphics card that also implements the interface. By following the interface everything works perfectly right out of the box.

For another example, let's say I implement an event bus in my system. I want to be able to broadcast "Here is the event" and have everyone listen. I don't care what systems are listening, they can be logging systems, they can be networking systems, they can be debuggers, they can be game event handlers, they can be anything else. All I care is that they implement the interface called IEventBusListener. If someone six months from now, or six years from now, decides to implement some code that implements the IEventBusListener interface they can add it to the code base and it will work perfectly.

For another example, let's say I'm working on a game and I want to find the nearest thing in the map that is a weapons spawner. The foolish way to do that is to look for a specific list of weapons spawners that the developer happened to know about when the code was written. If sometime later somebody adds another type of weapon they need to search for all the places in the code that relate to the weapons and hope they update all of them correctly. It is impossible for someone to come along and add a new spawner type without modifying all the source code. The smarter way to do it is to provide an interface, perhaps IWeaponSpawner, and search the map for anything nearby that implements that interface. If sometime later someone adds another type of weapon in the world it is automatically hooked up and completely integrated into the system. (This works just as well for component based or ECS systems, some happen to use a slightly different identification method rather than inheriting an interface, but the overall design is identical. Look for something that implements the component, something that implements the interface, something that claims it does the action, rather than looking for a specific list of items that you happened to know about at implementation time.)

When you write code, always write code that depends on the abstract types, on the interfaces. Only use the operations provided in the abstract types, on the interfaces. This helps all development down the road and new code can just drop into place.

Say I have a class GameObject, being the abstract base class for everything in my game. Something in the game might be a visible physics object, so if I want an object that does that using Interfaces, I would write a Renderable interface and a Physics interface and implement both. But say I have an object which doesn't use physics. I would have it implement just the Renderable interface.

You could do that, but most systems don't.

As mentioned above, this is a HAS A interface. This is composition, not an interface.

A game object does not satisfy IS A. Trying to replace them doesn't work. Swapping it out, a game object IS A model doesn't make sense.

A game object HAS A model. Or maybe it doesn't. Or maybe a game object has several models. Maybe there are models for "Full", "2/3 Full", "1/3 Full", and "Empty". Maybe there are models for "Base Started", "Base 10% Complete", "Base 20% Complete", "Base 30% Complete" ... "Base 100% Complete". Maybe there are models based on damage taken, or based on state of repair. You might implement an interface that says your object potentially has a model and request a reference.

Again, your game object does not implement the interface to be rendered. It is not a thing to be rendered. You should not be able to swap it out with any other thing that could be rendered. It may expose an interface to provide a reference to a renderable object, but it is not renderable itself.

Similarly with physics, a game object HAS A physics shape. Or maybe it doesn't. Or maybe the game object -- a character -- has a capsule physics shape when running, a sphere physics shape when squatting, and no physics shape when deceased. Once again, you might implement an interface that says your object potential has a physics object and request a reference to it, but it is a HAS A rather than an IS A relationship.

Why are Interfaces used instead of just Inheritance alone?

There are a set of programming principles under the great acronym SOLID. Through a bunch of trial and error, people have discovered that when you implement these principles in your code designs it goes a long, long way in keeping your code maintainable. It helps reduce bugs. It helps make it easy to swap out systems, to extend systems, to reuse systems.

Interfaces enable several of those principles.

Yes, interfaces have a small cost of a few nanoseconds when they are followed. If there is only one concrete type and there will only ever be one concrete type you should consider just using the concrete types directly rather than using virtual dispatch as interfaces or abstract types use. For Java, that means marking this as final. The O in solid, the Open/Closed principle, means to keep things open for for extension (meaning you can make new behaviors and extend on it but it is also closed that it must implement exactly the behavior of the base or abstract type, and be completely interchangeable with it. If you are not following this rule, when something is closed to both, make sure you make it completely concrete, and in Java that means marking it all over with final so you're not paying the cost since java is virtual by default.

However, any time you want replaceable parts -- and you almost always want replaceable parts -- it is the cheapest method available because every system out there has heavily optimized the pattern.


Use interfaces or abstract base types or whatever they are called in the various other languages when you need it, and you almost always need it. They are a great solution to many problems that every experienced programmer knows and every tool recognizes and makes faster for you.

My Renderer would need to take an object of type 'Renderer' - I think I see now where Interfaces fit in. I could make my object implement a 'Renderer' interface (but I'll not call it IRenderer, for fear of angering TheChubu... ) and implement a method to direct it to the renderer class. True, I'd need to duplicate those couple of lines but the actual heavy-lifting would be handled by the Renderer component.

That works well. For a first stab at game development, it wouldn't be half-bad, either. Remember, you _can_ still write games with heavy inheritance... for a small and simple enough game, it's even a good call.

The second part of the abstraction you should start to consider though is inverting your dependencies. There's no reason that your renderering system needs to know about GameObjects. GameObjects are renderable; they contain Renderables. Some GameObjects might contain no Renderables and others might contain _many_ Renderables. The only thing the renderer needs to know about is Renderables. You can therefor just have the GameObject tell the renderer about its Renderables and then get out of the picture.

In fact, this might even be automatic in the sense that a GameObject has to ask the renderer to create the Renderable objects. Therefor, no Renderable can exist without the renderer's knowledge so the renderer can automatically render everything without even knowing that GameObjects exist as a concept. This has further benefits down the road when you start studying more advanced rendering techniques: the renderer has to arrange its data in special ways to make rendering fast so keeping the renderer in complete control of the creation of Renderer objects becomes rather important.

This inversion of dependencies extends to a related problem that rendering in a component-based engine will incur: transforms (positions). Your physics code needs to move objects. Your graphics code needs to know where objects are to draw them. Your game logic needs to know where objects are to spawn bullets and such in the correct locations. Data like your transform/position thus becomes a rather hot commodity. There's a simple way to solve this: make a Transform before anything else and pass that to the constructors for your Physics, Renderable, and Logic components. Now they can all share transform information without having to know about each other and without having to know about GameObjects. There are more complicated/advanced solutions here that become useful for higher-performance "big" games but the shared Transform works perfectly well for brand new AAA games too (it all depends on your game's specific needs, of course).

The usefulness of this inversion of dependencies is so common it even has a name: Dependency Inversion Principle (didn't see that coming, did you? :P ).

Some pseudo code, since I know some people think better that way:


GameObject {
  Transform* _transform;
  IRenderable* _renderable;
  IBody* _body;
 
  GameObject(World& world) {
    // create the transform node that physics and graphics "communicate" through
    _transform = world.scene.createTransformNode(/*initial position*/);
 
    // create the interesting objects that make objects react and be visible
    _body = world.physics.createBody(transform, /*mass and other properties*/);
    _renderable = world.renderer.createRenderable(_transform, /*model filename*/);
  }
}

That's very simplified of course, but the concepts will work just fine for a large percentage of games.

Sean Middleditch – Game Systems Engineer – Join my team!

So, the GameObject itself contains almost no data or functionality, except that needed to request and store pointers to the various components? It's basically just a collection (small 'c') of components?

Almost everything it can 'do' is actually done by the components, which themselves are stored and assigned as necessary by the relevant logic units (renderer, physics iterator, game logic etc)?


So if I had, say, a ball. The 'Ball' object needs to be drawn, needs to move in the world, and needs to react to its surroundings.
When it's created, it would request a 'Renderable' component from the Renderer, which would create one, store it and hand a pointer to the new Ball object. The ball could then configure this accordingly and store the pointer.
Now the renderer can happily get on with drawing whatever it has in its list without ever needing to bother with the actual GameObject to which each renderable has been attached.
The same applies to the physics and game logic components.

Am I getting closer?

So, the GameObject itself contains almost no data or functionality, except that needed to request and store pointers to the various components? It's basically just a collection (small 'c') of components?

Almost everything it can 'do' is actually done by the components, which themselves are stored and assigned as necessary by the relevant logic units (renderer, physics iterator, game logic etc)?

That is the ultimate evolution of component-based designs, yes.

There are some small bits the GameObject might be in charge of. Not all of its components need to be pointers, either, depending on your specific game's needs or your design sensibilities. The ECS pattern goes so far as to say that there should be no such thing as a GameObject class and that a GameObject ("Entity") should just be a numeric identifier used to associate Components. (I'm not a fan of the ECS approach and could go on at some length about why it's silly, but I'll spare you.)

Am I getting closer?

Definitely looks like. :)

I feel I need to restate that this all depends on the game. I would (and have) laughed heartily at people trying to build a component-based engine to clone Pong or Breakout, for example.

I'll throw one last bit at you before I feel like we've perhaps overloaded a beginner. ;) That last bit is dynamicism or data-driven game objects. In your example, the Ball requests a Renderable. How does the Ball _know_ that it needs a Renderable? How does it know which model/sprite to request?

One possible answer to that question is to put that decision into data.


// ball.script
transform = createTransform([0, 10, 2] /*position*/);
body = createPhysicsBody(transform, 1.0 /*kilograms*/);
sprite = createRenderableSprite(transform, 'ball.png' /*image file*/);
 
createObject('Ball', [transform, body, sprite])

Now if you need to modify Ball's properties or create a new Box object or whatever, you can do so without having to modify a single line of "actual" code and you don't need to recompile your game. You can also make a graphical object editor that reads the scripts, let's users edit the objects, then resaves the modified object as script. Cool stuff.

Even if you don't want to add scripting support and all that (it's a lot of work, esp. for a beginner) you should at least structure your native code like that. Make functions to create a type of GameObject instead of making the GameObject or a subclass do the work of configuration. That will keep your GameObject and Component systems simpler and your game more modular. You can move those hard-coded functions into script later very easily.

Sean Middleditch – Game Systems Engineer – Join my team!

This topic is closed to new replies.

Advertisement