views:

258

answers:

6

Hi, I'm currently working on a Java trading card game, similar to the old Pokémon one. What I now want to do is to define all the cards in some way, but because there's a lot of fields that need to be initialized, I'm thinking of alternative ways, because a constructor will be very long and hardly readable for each card. I also have to initialize the attacks which means I have to basically create an anonymous inner class (is the term correct?) every time, like so:

/**
 * Base set Abra 43/102
 */
public final class Abra extends Pokemon 
{

    public Abra() 
    {
     super(
       new ImageIcon("img/scans/base-set/43-abra.jpg"), 
       "Abra", 
       "Base Set Abra",
       null, 
       Type.PSYCHIC, 
       Type.PSYCHIC, 
       Type.NONE, 
       30, 
       0
     );

     attack1 = new Attack("Psyshock", Type.NORMAL) 
     {

      /**
       * 10 damage. Flip a coin. If heads, the Defending Pokémon is now Paralyzed.
       */
      public void doAttack() 
      {
       damageApplyWeaknessAndResistance(10);
       if (gui.frames.CoinFlipDialog.showCoinFlipFrame() == CoinFlip.COIN_HEADS) 
       {
        Game.getOpponentPlayer().getActivePokemon().status = Status.Paralyzed;
       }
      }
     };

     attack2 = null;
    }
}

So my second option is to make a hierarchy with interfaces and abstract classes, meaning that the values will not be stored in fields, but rather just returned by methods when needed:

public interface Card extends Cloneable, MouseListener, MouseMotionListener
{
    public String getFullName();

    public ImageIcon getSmallIcon();

    public ImageIcon getFullIcon();

}
public interface Pokemon extends Card 
{
    public String getName();

    public int getHPLeft();

    public int getMaxHP();

    public Type getType();

    public Type getWeakness();

    public Type getResistance();

    public int getRetreatCost();

    public Attack getAttack1();

    public Attack getAttack2();
}

public class Abra extends AbstractPokemon 
{

    @Override
    public Attack getAttack1() 
    {
     return new Abra.PsyShock();
    }

    @Override
    public Attack getAttack2() 
    {
     return null;
    }


    @Override
    public int getMaxHP() 
    {
     return 30;
    }

    @Override
    public String getName() 
    {
     return "Base Set Abra";
    } //etc...

So my question is: Is any of these methods preferred or is there even any better way?

+2  A: 

I'd take the following approach:

Have a class that can act as a wrapper for any particular card. Find a way to export the data for each card into a file or database, and load the cards from the files/database when the program launches. The wrapper should be able to import all card-specific data... the wrapper will have all card-handling functions available, and some functions may not be applicable to all cards.

The alternative would be to have a card interface, and you design a custom cards using the interface, one new class for every card.

Depending on how expandable / flexible you want your engine to be, decide on an approach to take. I'd personally recommend using a wrapper class, and linking your engine to a database or flatfile.

Zachery Delafosse
+5  A: 

I recommend using the Builder pattern. Click here for an explanation.

It comes recommended by Josh Bloch: it's Item 2 in his book Effective Java 2nd Edition.

jqno
+1. Came here to say this
KitsuneYMG
The Builder pattern looks quite interesting and might be suitable for my needs. I will try it out.
pg-robban
In addition, I'd suggest moving the data into metadata files (xml or properties files probably). Give some unique identifier to each card and have your builder create it from the data in the file by looking up the data keyed on the unique identifier. You could even load lazily if you needed to, which might be useful if every card had an image and it was slow to preload all the images.
Chris Kessel
So now I have been experimenting with the Builder pattern a bit and although it would be great if I had just static Cards, it doesn't really "feel right" when I want to add in ever-changing fields like HP, plus that the Attacks would be difficult to implement using this method (or so I think).I reverted back to using interfaces like Zachery proposed, with some slight modifications which should work for now. I am however considering XML in combination with/or some scripting. I just want to say thanks to all who have taken their time to respond.
pg-robban
You might be able to solve it also by creating a single method "attack" interface, and make an instance field of this interface in your Card class. As for the HP field, of course it needn't be final. You can still initialize it with the builder, though.
jqno
I am using an Attack interface as you said, although the instance fields are defined in the AbstractPokemon class. The problem is now that in each class defining a Pokémon, I need inner classes defining the attack. As this leads to weird semantic errors such as "return new Abra.PsyShock" as well as having to create new objects all the time may not be a good approach. Can you suggest any improvements here?
pg-robban
Maybe you can pass the instance of the Pokemon card as a parameter to the methods of the Attack interface? That way, at least, the semantics will get untangled a bit. You won't have to use all kinds of weird inner classes to get to the cards' instance fields that way.
jqno
I'm a bit confused by that method: Can you give me an example of how to instantiate the Attacks that way? I can't really see right now how it eliminates the inner classes.
pg-robban
A: 

Why not just put all the info about a single entity in a hash table and wrap that in a class?

You still get encapsulation and all the nice advantages, but you can also easily write code to bind your data to a GUI or database without resorting to reflection.

You CAN still write setters and getters where they are needed to interact with other code, but in an app like you are talking about most of the fields are pure data that is never specifically manipulated (most code accessing fields is general copy and paste, not really specific to any field).

You can also use a single method like set("Name", "Abra"); or name=get("Name"); to access any field in the hash without writing dozens of setters and getters...

I came to this conclusion after writing hundreds of properties type screens where the data was simply being pulled from a DB, presented on a screen, modified, then sent back to the DB. These days my goal is that when I'm doing this I should be able to add a new control to the process without a single line of code--just metadata modification.

Bindings to the screen and database and even validations can all be set up as metadata then, and everything becomes much easier...

(Easier assuming you are as adverse to copy and paste as I am anyway...)

Bill K
Gotta love those -1s without justification. I know it's an unusual viewpoint, but I've been at this for decades and sometimes I don't think the typical way is the best way. If you really think it's wrong, why not say why? Meh.
Bill K
A: 

I would recommend using parametric polymorphism, with one card behaving according to how it has been configured. Not only would you reduce the number of classes (and by extension, complexity), but you would also be able to configure cards by passing their characteristics (wrapped in a structure, say, XML) to the constructor. You could also use this parametric polymorphism concept on the "attack" classes.

jeyoung
A: 

I would definitely take a data driven approach. Each card is going to share a certain set of attributes. You definitely don't want to implement a new builder or java class for each of your cards, especially if there could be hundreds of them. You will want your data in some format you can read and parse to create your deck so you can add, remove, and modify cards without having to modify java code. Something human readable/editable like xml would probably work very well.

The difficulty will be when it comes to special attacks or other items that require special code to handle. In that case, you could use an embedded scripting engine like jython or even the built in javascript support in java 1.6. This way you can modify your card library easily. The biggest difficulty now would be testing your special attack scripts.

digitaljoel
A: 

Gotta love those -1s without justification. I know it's an unusual viewpoint, but I've been at this for decades and sometimes I don't think the typical way is the best way. If you really think it's wrong, why not say why? Meh.

I am not the one who neg-scored you, but my take on why I would do so is as follows.

While your idea of using a hashtable to store internal data is feasible, it is not the best approach for what the original poster intends. His application (or game) involves a lot of operations based on the attributes of the card in the play; it is not only about saving the attributes to and reading them from a database. In such a case, having typed attributes with proper meaning will make it easier to perform the game logic. Remember that OOD/OOP is about proper encapsulation and having a cohesive and meaningful structure.

jeyoung