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?
3
A couple of thoughts:
-
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.
-
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(); } }
-
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.)
5
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.
1
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.