2

I'm currently working on a group project to recreate the original Pokemon Yellow version in Java. We've only just started planning it out but they way we've drawn out the design right now is to have an abstract Pokemon class and have 151 (Count of the original Pokemon) classes that extend it, each one being a different Pokemon. Each class will be able to store data such as an ID, its name, the type of Pokemon it is (ie: rock/water/fire/etc), and the moves it can use and learn. It seems a little much to be creating 151 classes for the Pokemon but it also seems like a good way to limit coupling. As far as design goes is this a good way to go about doing it, if not what are some better methods to go about doing it?

5
  • 13
    Definitely do NOT create 151 classes.
    – Rotem
    Nov 17, 2013 at 9:03
  • Make 1 class and make moves a collection property. Everything else will be config (i.e. which combinations of properties constitute the 151 valid pokemon types).
    – Tom Tom
    Nov 17, 2013 at 10:34
  • 2
    I'm not sure why you think coupling is such a large concern here. Can you explain what's being coupled that making 151 subclasses deals with?
    – Joe Z.
    Nov 17, 2013 at 10:42
  • Pokemon are pretty similar each other, only values of their properties change from pokemon to pokemon. I think it would be better have a base class pokemon which properties can be setted to create a particular type of pokemon. Then you can have a factory method to instantiate a particular type of pokemon like: Pokemon PokemonBreeder.giveBirthTo(String pokemonName, int level); Nov 17, 2013 at 12:18
  • I can see where the draw of 151 classes came from: All Pikachu, for example, have base stats that are the same, but each individual Pikachu has some variation. They also share the same evolutionary line, and the techniques they learn at level up. The alternative - one class with various attributes - would mean massive lookup tables to get base stats, techniques, and so on. Each instance would only need to hold that individual pokemon's variation, then. 'Tho there's probably still a better way.
    – Izkata
    Nov 17, 2013 at 16:41

5 Answers 5

6

You don't really explain why your group thought it would be a good idea to (hopefully) generate 151 classes, as "to reduce coupling" doesn't really make sense yet as you had no design or code and therefore no coupling.

Each class will be able to store data such as an ID, its name, the type of Pokemon it is (ie: rock/water/fire/etc), and the moves it can use and learn.

Change 'class' to 'instance' in that sentence and you get why you only need to create one Pokemon class: they all share the same properties. A derived class should add or implement properties (and / or methods) unique to that derivation.

If you define the properties of ElementType, Move and PokemonBase as such:

enum ElementType
{
    Fire = 1,
    Water = 2,
    Grass = 4,
    Rock = 8,
}

class Move
{
    int ID;
    string Name;
    ElementType ElementType;
    int BaseDamage;
}

class PokemonBase
{
    int ID;
    string Name;
    ElementType ElementTypes;
    List<Move> Moves;

    decimal HitPointsPerLevel;
    decimal AttackPointsPerLevel;
}

Then your various instances only differ in what is in those properties. Your class and code should not care about that. A Pokemon that has the type Fire can be stored in exactly the same class as a Pokemon of type Water: they only carry a different value of their ElementTypes property.

You can now fill a list of Pokemon instances with their properties and moves, for example by loading a text file or reading from a database, in a format you like.

You can solve all those problems when loading the Pokemon. In the data source you can store additional properties, which in the case of "the moves it can use and learn" can be a relational table (or flat text equivalent) where you join Pokemon.ID, Move.ID and Level to each other (if memory serves me right, they learn a new move on reaching preset levels), storing it in a list or dictionary property in your Pokemon class.

Or perhaps while designing you thought you needed a different class to implement the fighting system differently for each Pokémon. You don't have to, as by combining the various properties you can give each instance its unique properties.


The problem of course starts when you realize that you actually have two types of Pokemon: a 'blueprint' for all Pokemon that can possibly occur in the game, and an actual Pokemon that a player carries, which has a Level, HitPoints, and so on, all properties depending on or calculated through the Level or based on the fact that the isntance is "in the game". You can implement those properties in a derived class:

class Pokemon : PokemonBase
{
    int Level;
    int MaxHitPoints;
    int HitPoints;

    // Stats, for example
    int Attack { get { return Level * base.AttackPointsPerLevel } };
    int Defense;

    // This method directly applies the attack results to the incoming Pokemon, by reference.
    int Attack(Pokemon other, Move attack)
    {       
        int damageDone = (this.Attack * attack.BaseDamage) - defender.Defense;

        // TODO: http://bulbapedia.bulbagarden.net/wiki/Type_chart
        decimal modifier = CalculateEffectiveness(other, attack);
        damageDone *= modifier;

        other.HitPoints -= damageDone;      

        return damageDone;
    }
}

And all Pokemon instances can use it, without having to derive anything. The key is modeling your class and method so, that the actions you want to undertake on each of them can be applied to all combinations of types. You can also for exampla add a List<DamageOverTimeAttack> to a Pokemon to implement attacks that should stay active for a few turns, handling it in the Attack() method.

Now when a player encounters a new Pokemon, you might want create that from a Factory:

class PokemonFactory
{
    List<PokemonBase> _allPokemon;

    Pokemon GetPokemon(id, level)
    {
        blueprint = _allPokemon.Find(id);

        return new Pokemon
        {
            ID = blueprint.ID,
            Name = blueprint.Name,
            ...
            Level = level,
            ...
        };
    }       
}
0
2

do different pokemons should expose different APIs in your code? You should analyze this first.

Their ID, name & type can be stored in members. Now you could say that they have different abilities. So each ability should be a method. But you would be wrong. Doing so would prevent you from writing simple code when managing combat.

You should abstract an ability in a separate class. It may be defined by its cost & effect. Then each creature has a List of ability. Your 151 pokemons are defined in a service method or a config file. When writing features, the code should never care what type of pokemon is passed.

151 class for modeling something is a huge complexity cost. Most likely, you would have to use reflection to inspect data relevant to one particular class. This would be a misuse of the java class system.

3
  • Abilities didn't come around until Pokémon Ruby/Sapphire, so they're out of the equation.
    – Joe Z.
    Nov 17, 2013 at 10:23
  • I am not a domain expert. I was just making up an example. Nov 17, 2013 at 10:26
  • Oh, okay. It's a valid concern in any case, for things like moves as well.
    – Joe Z.
    Nov 17, 2013 at 10:34
1

I believe that if you have a base pokemon type, it may be easier to derive only the fire,water,electric types from the base class to limit the number of different classes needed. From that, you can set the relationships between the pokemon types without too much hassle, eg. Fire beats water and grass, weak to electric etc.

From there you can probably have a database or JSON/XML file that stores all of the stats of each pokemom, ID, name and which evolution it is from. This format could definitely help with grouping and classing the data without much overhead at all. The data can then be pulled by each pokemon type class once the program initializes by reading the JSON/XML file in sequence. A whole lot of that process can be automated once you've set up all of the stats in the file-store. From there, accessing each pokemon should be simples!

1
  • 3
    Why would he need a separate class for fire/water/etc? I could see maybe having a "elemental affinity" class (or an enum). However, from a programming standpoint I would consider "elemental affinity" to be a property of pokemon, rather than a type of pokemon.
    – Brian
    Nov 17, 2013 at 9:26
0

I'm not sure why you would need 151 separate classes to store the data for each Pokémon.

If I'm reading your intent correctly, you want to have 151 classes, one to store the base data for each species of Pokémon (ostensibly as static data for the class). But this is hopelessly inelegant. If you just need a structure to store the base data for each Pokémon, why not have something like a PokemonBaseData class?

You could structure it like this (forgive me if the syntax is incorrect, my Java is a little rusty):

public class PokemonBaseData {

    public int ID;
    public int type1, type2;
    public int height_cm, weight_hg;
    public int base_HP, base_AT, base_DF, base_SA, base_SD, base_SP;
    public Vector<Integer> usable_moves; // or move IDs
    // other properties as you desire them here

}

And have each Pokémon of a certain species ID refer to the PokemonBaseData class with a matching ID for the data inherent to that species.

0

Have you considered the flyweight pattern? That would allow you to avoid creating the 151 classes and still have the required functionality.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.