3

Basically, what I would like to obtain is a way to iterate through ONE list, and call methods specific to the interface the objects in the collection implement.

In my Java project, it would result in something like this:

//GameComponent would be my empty abstract class  
HashSet<GameComponent> components = new HashSet<GameComponent>(); 
components.add(/* instance of a class that extends GameComponent and implements Drawable || Updatable */);  

for (Drawable d : components)
    d.draw();

for (Updatable u : components)
    u.update();

My friends suggested me to just make two separate lists, but that means some objects will be in both lists.
I also think that it makes sense (abstraction-wise) to have one list of GameComponents and iterate through that one list.
But I also don't like having an empty class to inherit from, only for categorisation.

So, the question is: does my approach make sense? If so, is it elegant? If not, what pattern could I follow?

  • I recently created a game, and my solution to this was to supply update and draw behaviour via the strategy pattern. An object which only draws itself could have a no-op update strategy, and vice versa. – Phoshi Apr 10 '14 at 12:12
  • @Phoshi Not a terrible solution, but it forces the object to know how it's being used. That is, you've changed the interface of game objects because one particular use case of them was to draw them. That's kinda like adding a sort method to the List interface just because sometimes you want to sort lists. – Doval Apr 10 '14 at 12:26
  • @Doval: Completely correct, of course, and actually something I'm fiddling around with trying to fix (currently via a typesafe messaging system). In this case, however, I think it's fairly inherent to the concept of an entity in a video game that it has some behaviour and some visibility (Though you're absolutely right that this isn't always the case. See controller objects or dumb UI elements). Still, it's a solution which works well enough in practice, and is very straightforward, so worth mentioning. – Phoshi Apr 10 '14 at 12:31
3

A couple of thoughts:

  1. It's better if game components don't know how to draw themselves. A class should have only one reason to change; here your components may change if you need to change what they do in-game, and they could change if you need to change how they're drawn as well.

  2. For this scenario the most straightforward solution is use instanceof. It's not the most general solution but it's the simplest thing that'll solve this particular problem.

    for (GameComponent component : gameComponents) {
        if (component instanceof Drawable) {
            ((Drawable) component).draw();
        }
        if (component instanceof Updatable) {
            ((Updatable) component).update();
        }
    }
    
  3. If you have a finite set of GameComponent types, what you want is a sum type, which mainstream OOP languages don't provide native support for but you can kind of emulate through the Visitor Pattern. (There's an alternative way of implementing them but it won't be elegant in Java; it works best with named arguments and lambda expressions.) This is a more general solution to taking decisions based on the particular kind of GameComponent because it gives you full access to the component type.

It would look something like this:

    public interface ComponentVisitor {
         void visit(Foo foo);
         void visit(Bar bar);
         void visit(Baz baz);
    }

    public abstract class GameComponent {
        // prevent subclassing outside this scope
        private GameComponent() {}

        public abstract void accept(ComponentVisitor visitor);

        public static final class Foo extends GameComponent implements Drawable, Updatable {
            // implement ...

            public void accept(Componentvisitor visitor) {
                visitor.visit(this);
            }
        }

        public static final class Bar extends GameComponent implements Updatable {
            // implement ...

            public void accept(Componentvisitor visitor) {
                visitor.visit(this);
            }
        }

        public static final class Baz extends GameComponent {
            // implement ...

            public void accept(Componentvisitor visitor) {
                visitor.visit(this);
            }
        }
    }

Now you can take component-specific decisions like this:

    for (GameComponent component : gameComponents) {
        component.accept(new ComponentVisitor() {
            public void visit(Foo foo) {
                // do something Foo-specific here
            }

            public void visit(Bar bar) {
                // do something Bar-specific here
            }

            public void visit(Baz baz) {
                // do something Baz-specific here
            }
        });
    }

This works because any component needs to implement accept, and for visit(this) to compile the Visitor needs to have a method that accepts that particular kind of component. It's still possible to implement a component such that accept doesn't call visit(this), so we safeguard against that by using a private constructor. That prevents outside subclassing while allowing subclassing of inner classes (because inner classes have access to private members of their enclosing class.) The end result is a finite set of components and an exhaustive way to discriminate between them (you can't forget to handle one of them because the Visitor implementation wouldn't compile.)

  • 1) I find it quite logical to keep the draw code in the specific classes. If I need to know how my Car object is drawn, I just open my Car class. However I do understand it is good practice to decouple the draw logic from the update logic, although I have no idea how to do it cleanly. – Carlo Vespa Apr 10 '14 at 23:26
  • PS: 3) I've never used the visitor pattern and, from the code you've supplied, it just seems "too much" for what I intend to do and also point 2 seems even cleaner, in my opinion. What are the long term benefits? Is the added complexity worth the effort? – Carlo Vespa Apr 10 '14 at 23:35
  • @CarloVespa To draw a game object you just need to know three things: 1) what kind of object it is, 2) what state it's in, and 3) how long it's been in that state. You can change your Drawable interface such that instead of the object drawing itself, it provides that information. Values 1 and 2 can be enums. Regarding the Visitor pattern, it is indeed verbose; this is a shortcoming of the Java language. The benefit is that if you ever need to take a decision based on the class of GameComponent, the Visitor Pattern ensures you handle all cases... – Doval Apr 11 '14 at 12:38
  • @CarloVespa ... whether it's worth the extra verbosity is honestly up to you. It's also noteworthy that the visitor pattern doesn't exactly solve the same problem as point 2. With the visitor pattern you take decisions based on the class of GameComponent, while in point 2 you're branching based on their interfaces/functionality. With the Visitor Pattern there's a finite set of subclasses and you ensure you handle all of them. But classes can implement an arbitrary number of interfaces and it doesn't make sense to try to exhaustively check all of them. – Doval Apr 11 '14 at 12:42
  • Thank you very much for the insightful comments. I'll keep the Visitor pattern in consideration, but I'll first try to refactor the class structure of my project. I will post an answer if I find a satisfactory solution. – Carlo Vespa Apr 12 '14 at 14:45
0

A few things to consider:

  • Why is GameComponent empty? If there are any methods/properties that are shared between all children of GameComponent it wouldn't have to be an empty abstract base class.

  • Does GameComponent have to be a class? If you're not defining any default behaviour or values in it, you could just as well make it an interface.

An empty interface is not neater/more elegant per se, but using interfaces to 'tag' an object as being of a certain type let's it keep its ability to extend another class.

  • No, you are right, it doesn't have to be a class; but I guess the "style" problem is still open. – Carlo Vespa Apr 10 '14 at 11:46
0

I see no problems with this.

For example, when I login I'll end up with a login result:

public abstract class LoginResult { ... }

When I login, I might have three scenarios:

  • Login failed as password doesn't match
  • Login succeeded
  • Login succeeded, but account is banned

To accomplish this, I would then introduce three classes that inherit from the base:

public class SuccessfulLoginResult : LoginResult 
{
   // May contain a string with a welcome message
}

One for when login fails:

public class FailedLoginResult : LoginResult
{
    // String containing information as to why the login has failed and integer property indicating how many login attempts are left
}

One for when account is banned:

public class AccountSuspendedLoginResult : LoginResult
{
   // String containing information as to why account is suspended and potentially a date time field indicating when account will be activated again
}

I would then use it in the following way (C#):

var loginResult = _accounts.Login("Username", "Password");

if (loginResult is SuccessfuLoginResult)
{
   // Do something
}
else if (loginResult is FailedLoginResult)
{
   // Do something else
}

Base class can stay empty and I see no problems with that.

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.