I'm trying to abstract away the Image and ImageList classes, and I'm forced to ask myself:
What are images doing in the core of the game? It's not like it can display them, or even manipulate them – they are part of the UI, and that's the only part of the application where they have anything to do!
So why is the Image type known in the application, really?
Found it:
Ship - 4 images are used to define the graphical representation, depending on the state.I've solved this problem once before, on a completely different project:
Each icon-able object implemented the Iconable interface, which defined one method:
ApplicationIcon getIcon();
and ApplicationIcon was a tag interface - it had no methods. It was meant for humans, and humans had to make sure the objects implementing it were, as it was defined, displayable by the graphics layer.
The implementation was basically the Strategy design pattern:
A static field in each class was set, at initialization time, to a specific icon-producing object, which was implemented in the graphics layer.
Each Iconable object would invoke its type's icon producer when asked to give an icon.
public class SomethingWithIcon implements Iconable { /** Set to an appropriate factory by the InitilizeIcons class. */ private static IconFactory<SomethingWithIcon> iconFactory = null; public ApplicationIcon getIcon(){ return iconFactory.makeIconFor(this); } }
public class SpecializedIconFactory implements IconFactory<SomethingWithIcon>{ public ApplicationIcon makeIconFor(SomethingWithIcon target){ if (target.isRed()) return loadImage("redSquare.png"); else return loadImage("yellowTriangle.png"); } }
I'm guessing that if my language had a good module system I could have avoided the initialization-time manual linkage.
Something still bothers me about this design: Why does any object in the game have a getIcon() method? It's not exactly in the responsibility of said object, is it? What if the future UI would be voice-based, would we add a getVocalMotif()?
So, here's a slightly different approach:
Domain objects (i.e. Weapons, Ships, etc.) don't have a getIcon(), nor any other method that talks about representation. They have their state (data), methods that (logically) belong to them, and getters for data that may be displayed by the UI.
The UI may represent any domain object any way it sees fit, based on the data of said object. If it wants to show a picture - go ahead and figure out which.
Yet Another Problem: CustomShip, which has its custom image.
The image (or at least, some descriptor of it) needs to be saved and loaded along with the game. It’s impossible to ensure complete portability of this data anyway – one impelementation might only have audio and another only video, to extand the example above – so I’ll just do the simpelest thing that might work now, and handle the repercussions later...
The CustomShip object would have a text field representing the image, which the UI layer would manage, and the CustomShip will save and load. Just be aware that you might get something in strange formats when you use that field. And just to prepare for unlikely example I chose for today, I’ll call this field UIRepresantation.
Comments
Visitor
I believe a visitor that is able to supply an image for a given 'CustomShip' (whether it is dynamically created or just loaded from a pre-saved location) according to the ship's properties would serve you well.
that way you can have multiple visitors (image, sound, etc.) on your domain objects thus encapsulating layer specific logic outside of your domain objects.
Re: Visitor
Visitor is somewhat similar to the old solution, in that it forces an Accept() method in the subject (http://en.wikipedia.org/wiki/Visitor_pattern). I'm trying to "purify" the objects, so that goes against it.
I want the UI to feel free to display icons even for things that didn't expect to be displayed, or even aren't game object, which would otherwise require special treatment.
Actually an Accept method is
Actually an
Acceptmethod is only needed when visiting different object using a mutual interface (instead of usinginstanceofto determine what method signature to apply).otherwise (if you know the object's concrete class), the visitor can be applied directly on the object. I'm not sure if thats your case or not but i'll agree that "purifying" the domain object completely posses a real problem. However bear in mind that
Acceptmethods (Visitableinterface) can be implemented only in base classes and therefore usually aren't regarded as a big compromise or burden on your classes.