3

I'm trying to develop an OSGi application to handle my game engine. This game engine will be making use of the Entity-Component-System pattern. Part of this pattern is the use of GameSystems.

I had initially planned on exposing the GameSystem interface as its own bundle; this way other contributors can create their own systems and add them to the game engine via OSGi bundles. When I wrote up the initial interface it looked like

public interface GameSystem {

    boolean isInterestedIn(GameEntity entity);

    void update(GameEntity entity);
}

Then I wrote an abstract implementation of this to add some precondition checking (returning false if the entity is null).

public abstract class AbstractGameSystem implements GameSystem {

    public boolean isInterestedIn(GameEntity entity) {
        if (entity == null) {
            return false;
        }
        // TODO
    }
}

At this point I realized I needed some sort of mechanism for allowing the subclass to add their own conditions, without being forced to call super.isInterestedIn(entity). I created a protected abstract method that subclasses could implement.

public abstract class AbstractGameSystem implements GameSystem {

    protected abstract boolean checkEntity(GameEntity entity);

    public final boolean isInterestedIn(GameEntity entity) {
        if (entity == null) {
            return false;
        }
        return checkEntity(entity);
    }
}

At this point I take a step back and wonder if maybe I should, instead of depending on inheritance, pass a predicate and consumer as constructor parameters, and not bother with an abstract class at all.

public final class InjectedGameSystem implements GameSystem {

    private final Predicate<GameEntity> predicate;
    private final Consumer<GameEntity> consumer;

    public InjectedGameSystem(Predicate<GameEntity> predicate, Consumer<GameEntity> consumer) {
        this.predicate = Objects.requireNonNull(predicate);
        this.consumer = Objects.requireNonNull(consumer);
    }

    public boolean isInterestedIn(GameEntity entity) {
        return predicate.test(entity);
    }

    public void update(GameEntity entity) {
        consumer.accept(entity);
    }
}

The expected execution of this code would be in the game engine, something like

public void run() {
    while (shouldRun()) {
        for (GameEntity entity : entities) {
            for (GameSystem system : systems) {
                if (system.isInterestedIn(entity)) {
                    system.update(entity);
                }
            }
        }
    }
}

This revealed an interesting question in my head. If I always expect a piece of code to be run in a certain way (call isInterestedIn followed by update if it returns true), and if I provide a way of delegating all functionality to other objects (the predicate and consumer), is there any reason to provide the interface at all, instead of just the delegating class?

0

A class like InjectedGameSystem that just delegates all it's methods essentially is an interface. The difference is that you can assemble the implementation at runtime, by providing appropriate delegate functions.

Whether it makes sense for such a class to implement an interface depends on the usage. I can think of three potential reasons for having a separate interface:

  • You have to comply with an existing interface, because that is required by a consumer of the class.

    For example, I often apply your approach to dynamically create objects that implement the ICommand interface in .NET. Clearly, this is not the case for you.

  • You have implementations that act as GameSystem, but also need to do other things, and possibly inherit from other classes.

    Again, from what you've written, it doesn't seem like you need that. Also, you can always get around that, by having that class provide a InjectedGameSystem AsGameSystem() method.

  • You want to hide the fact that the method calls are delegated, because this is an implementation detail. This is more relevant if creating and consuming the objects happens in different modules.

Personally, I would probably keep the interface, but I think it would be hard to make a case that one approach is significantly better or worse than the other.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.