0

I'm designing a card game (think Magic the Gathering for purposes of this example) and want to take the information for the cards and store it in a database. In this game, there are events (for instance, one card might say "when this comes into play, opponent takes 2 damage") that are tied to a particular card. The design decisions have led to loosely building the cards in a builder factory, but I'm looking to take the cards and store them in a database instead. Since most of the cards are instances of a base "Card" class, it's easy to load the features common to every card (name, cost, etc.) but I've struggled to find a good way to tie these events to a single type of card. The only way I have thought of so far was to store the function name in the database and use late binding to register the event when the card is loaded. Is there a better way to do this?

The only similar post I've found is this: Store function name in database and then execute it

The answer of using eval() seems similar to late binding, but got down-voted. However, no one had a better suggestion how to perform this function.

Community
  • 1
  • 1
Mike Beck
  • 35
  • 4
  • Showing the class attributes you have already (or table fields) would be helpful. – David Tansey Apr 15 '13 at 05:50
  • I don't see how, but it's all standard so far. Things like: byte power byte armor byte speed string name – Mike Beck Apr 15 '13 at 06:01
  • That's probably enough. I'm trying to understand the appeal of storing specific function names with the card data and I confess I'm not really getting it. I'm seriously curious what function name would be stored for your example? – David Tansey Apr 15 '13 at 06:14
  • For this example ("when this comes into play, opponent takes 2 damage") the function would be DamageOpponent(2) where 2 was a parameter. – Mike Beck Apr 15 '13 at 06:17
  • Excellent. It seems to me that you ought to be able to implement this behavior in the base class. Either a null or zero in this attribute would mean no damage to opponent (but in this example the attribute value would be 2 not zero). Following a pattern roughly like that you could have simple and complex card 'events' modeled via combinations of the attributes and should haven't to subclass your base too many times -- but I'm guessing. – David Tansey Apr 15 '13 at 06:25
  • This is a very interesting approach I hadn't considered. It would be very inefficient at the time of "creating" the cards but that only happens once per game at the start. How do I add rep to a comment? lol – Mike Beck Apr 15 '13 at 23:54

1 Answers1

0

This sounds like a usable approach, however it is questionable if it is a good one.

Why don't you construct propper objects from a card representation in the database? Either by using an object database, or, more likely, but using object-relation-mapping. THis way you can represent each type of card in a clean and easy to read and use way yet work with rich instances of specialized classes derived from a common base class.

So you use a common table to store all the cards like this:

+-------------------------------------------------+
| card type | data1 | data2 | data3 | ... | dataN |
+-------------------------------------------------+
| Card      | 123   | 456   | 789   | ... | abc   |
| CardS1    | 123   | 456   | 789   | ... | abc   |
| CardS3    | 123   | 456   | 789   | ... | abc   |
| CardS2    | 123   | 456   | 789   | ... | abc   |
...

And a class hierarchy like this:

+---------------------------+
| class Card                |>-+
+---------------------------+  |
| var data1                 |  |
| var data2                 |  |
| var data3                 |  |
| ...                       |  |
| var dataN                 |  |
| baseMethod1()             |  |
| baseMethod2()             |  |
+---------------------------+  |
                               |
+---------------------------+  |
| class CardS1. public Card |<-+
+---------------------------+  |
| specialMethod_1_1()       |  |
+---------------------------+  |
                               |
+---------------------------+  |
| class CardS2. public Card |<-+
+---------------------------+  |
| specialMethod_2_1()       |  |
| specialMethod_2_2()       |  |
+---------------------------+  |
                               |
+---------------------------+  |
| class CardS3. public Card |<-+
+---------------------------+
| specialMethod_3_1()       |
+---------------------------+
arkascha
  • 41,620
  • 7
  • 58
  • 90
  • I'm trying to avoid writing 1000+ classes that are all the same base class +/- 1 to 2 methods being registered. I'm looking at object-relation-mapping and not really seeing how it would allow me to represent functions/methods in the database. If I'm missing the idea, just let me know I'm an idiot, and if possible point me towards a site/book that would clear it up for me. The other fields of the card are all valid database types (bool, enum, byte, int, string) and aren't any difficulty translating into the database. – Mike Beck Apr 15 '13 at 06:07
  • 1
    The base class is only implemented a single time, all other classes are derived from that base class. That is the idea of a common base class. And you have to write the routines (methods) anyway, don't you? So what is the difference to putting them in a small and clean class each? About storing such an object: you don't store the method itself, but only the data values of instanciated objects (so not the implementation). The OR-mapping takes care that when you read an entry from the database a propper object is contructed from it that has the method as defined in the class implementation. – arkascha Apr 15 '13 at 06:13
  • The data fields backing each card is the same, the ONLY differences are the 1-2 methods. There is a very finite amount of methods (probably 50ish) vs substantially more cards, so those methods themselves wouldn't even exist in each individual card class. The only advantage to creating sub classes for each type of card would be, in the constructor I could register the necessary methods to the delegates stored in the class. However, as a downside, I now can't use a single factory method to create all of the cards unless that factory method takes the card name and late bind creates it. – Mike Beck Apr 15 '13 at 06:20
  • 1
    Certainly most data fields are common to all cards. That is the perfect situation. You make a table holding all those fields in separate columns. Then there is an additional column holding the card type. The OR-mapping creates objects by selecting the right class depending on that card-type-column. No overhead at all. If a card has no special methods you can simply instanciate the base class. It appears you did not really understand the concept of deriving and extending a class. You do NOT have to 'register' a method in a constructor or use late state bind. That is what deriving is all about. – arkascha Apr 15 '13 at 06:28
  • @arkascha has said what I'm trying to get across, but done a better job of it. – David Tansey Apr 15 '13 at 13:21
  • I understand how OOP and class hierarchy works, but every card has at least one of these special methods. The goal of moving to the database was to avoid creating a subclass for each card, but unless I can move these methods into the database I won't achieve this. – Mike Beck Apr 15 '13 at 23:52
  • OK, I just made a suggestion which reflects a typical approach to problems like this. The advantage: clean class hierarchy and easy maintainance later on. Don't get this wrong, but your strategy of "putting the method in the database" sounds like a pure procedural oriented approach and ignores all advantages of OOP by design. As said: you will have to implement the methods anyway, so you save nothing because the class containers would be a maybe 4 lines each and as said it can be automated using a templates. But it is your application :-) You are free to implement your strategy :-) Have fun! – arkascha Apr 16 '13 at 07:13