2

I am trying to develop a computer card game (something like Hearthstone but less complex). I have a table in a database with all the cards that I want in the game. The table has a column cardType that stores the card type, which should be the corresponding constructor for a java object. I want to instantiate an object of that type based on the table field.

For example: Let's say I have in my table a card with cardType = BasicMinion(7,7).

Then in Java I have a class:

BasicMinion extends Card { 
public BasicMinion(int health, int attack) {
// initialize
}
public void play(){
//do something
}
}

I can use reflection to create an object using the string I get from the database doing something like this: Java how to instantiate a class from string

However, my cards extend the class Card which has some basic events to which it can react. Will reflection allow me to call the methods from the base class Card and also the methods that the derived class has?

Basically I want to have for example an ArrayList<Card> where I can put all the card types I got from the database so I can call their methods when an event occurs.

I seem to be missing something. The question is pretty vague, but how should I do this?

Community
  • 1
  • 1
Andy
  • 155
  • 1
  • 13
  • Since the BasicMinion extends Card, you can call those methods on the objects after creation. I don't quite see why you need reflection to call the Card methods, since by that time you already have a BasicMinion? – Tom Cools Mar 18 '17 at 18:06
  • Sounds like you have a plan. You will store the name of the `class` in the database, as well as the the type of the constructor arguments and their values - how you do that is an exercise for you. You then use reflection to create an instance of `MyCard` as a `Card` and call methods via the `Card` interface. What's the issue? – Boris the Spider Mar 18 '17 at 18:07

3 Answers3

1

My cards extend the class Card which has some basic events to which it can react. Will reflection allow me to call the methods from the base class Card and also the methods that the derived class has?

Yes, once you create the object for the class using the string (like using Class.forName() and newInstance() methods as shown below), you can invoke the methods of the base class Card and the subclass BasicMinion using the reference.

Class card = Class.forName("com.myproject.Card");
Card cardObj = (Card)card.newInstance();
//invoke cardObj.method1();

Class basicMinion = Class.forName("com.myproject.BasicMinion");
BasicMinion basicMinionObj = (BasicMinion)basicMinion.newInstance();
//invoke basicMinionObj.method1();
Vasu
  • 21,832
  • 11
  • 51
  • 67
  • How do I do the cast to a specific card, if I only find the type after I get the string from the database? I mean if I don't know that it's a BasicMinion. – Andy Mar 18 '17 at 18:27
  • You can't, you have to know the type in order to cast. So, you'd need a switch() again. Making reflection useless :-) – Tom Cools Mar 18 '17 at 18:29
1

You instead could use a Factory (I know, Java and Factories), if you don't have many different card types (which you most likely don't). It will allow for clearer code and better exception handling options.

example:

public Card createCard(String definition) {
  String type = //do parsing of definition (string splitting etc)
  String parameters = //do parsing of definition

  switch(type) {
       case "BasicMinion": 
             Long param1 = ///do parsing of parameters
             Long param2 = ///do parsing of parameters;
             return new BasicMinion(param1, param2);
       //add others..
       default: throw new UnknownCardTypeException();
  }
}
Tom Cools
  • 1,098
  • 8
  • 23
  • I thought about doing it like this. But what would happen when a new card type gets added? Do i just add another case to the switch? – Andy Mar 18 '17 at 18:20
  • Jup, you need to recompile the application anyway, since you have created a new Card subclass. The "default: throw new UnknownCardTypeException();" will throw an exception during testing, so you can not add a new Card type without adding anything in this class. – Tom Cools Mar 18 '17 at 18:21
  • Reflection seems cool and a good idea, but it is very fragile. I prefer the method above since it makes very clear what types of cards you have, and how they are built. Reflection Error handling is a mess. – Tom Cools Mar 18 '17 at 18:23
  • I was going to add a sentence that reflection could/should be avoided altogether, but I see you already worked that part. But one suggestion: the parameters would probably depend on the type; so you want to move the parsing of those INTO the case body. But one note: the op is right ; such huge switch statements can turn into a maintenance nightmare quickly when you don't pay attention. So one really needs to know how to work with them. On the other hand reflection code is a maintenance nightmare starting day one... – GhostCat Mar 18 '17 at 18:28
  • This seems easier then using reflection. The thing is that if I have only 50 cards, but all of them are different, then I need50 case statements. But I guess it doesn't matter because I'll need one class for each card anyway in that case. – Andy Mar 18 '17 at 18:31
  • Why do you have 50 different cards? I guess you can change it a bit so you can reuse the same class a bit more. Example, add more parameters for special effect of cards? + Agree with GhostCat on parsing and Switch case nightmares. I believe a good Switch is the lesser of two evils when the amount of different cards is reasonable – Tom Cools Mar 18 '17 at 18:32
  • I don't have that many cards yet. I definitely plan to use some classes for more than one card by using a wide range of parameters. But if cards have vastly different effects I have no choice but to create new classes. – Andy Mar 18 '17 at 18:37
  • And just for the record: alternatively you could consider to serialize your objects completely. So instead of rebuilding them manually, you use some kind of framework to do that for you. Many options to explore here. – GhostCat Mar 18 '17 at 18:37
  • For the special effects, maybe you should generalize this. First Parse the Effects and map them to an Effect.class Then feed that Effect object into the constructor of your specific card. This is called the Strategy Design Pattern: https://sourcemaking.com/design_patterns/strategy – Tom Cools Mar 18 '17 at 18:38
0

You use reflection here to create new instances of classes.

After you have done that, you have ordinary objects, that are "the same" as if you had created them for the very first time.

In other words: you can use reflection to re-create your objects using reflection, but then there is no more need at all to fall back on reflection.

And finally: using instanceof and casts you could even call sub class specific methods. Well, you could doing so - but downcasts are often a smell of bad design; so when you are happy with the methods provided by the base Card class - all the better.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • So if I have for example an `ArrayList ` can I add the objects I get using reflection in this array and then just call the methods from the `Card` class on them? – Andy Mar 18 '17 at 18:16
  • Yes, once created it's a normal "Card" object, so you can add it to the ArrayList. Also look for my answer without using Reflection at all. – Tom Cools Mar 18 '17 at 18:18
  • Now I am really curious which of you guys took the upvote I saw here an hour ago ; especially given the fact that we had that good constructive discussion... – GhostCat Mar 18 '17 at 19:14