1

I am creating a small game of Final Fantasy characters, in which I input the name of who I would like to "fight" I have each character (only 5 of them) as a subclass to a superclass called Stats in which the variables (non static) and getters/setters are defined.

The code all works as I would like, but I don't like it all being in One huge class.

The Main method is here;

package com.George.revision;

import java.util.Random;

import com.George.characters.Cloud;
import com.George.characters.Squall;
import com.George.characters.Stats;
import com.George.characters.TheEnemy;
import com.George.characters.ThePlayer;
import com.George.characters.Tidus;
import com.George.characters.Yuna;
import com.George.characters.Zidane;
import com.George.input.GetInput;
import com.George.input.ListNames;



public class Main {


    public static void main(String[] args) {

        ListNames.listNames();


        Stats clo = new Cloud();
        Stats squ = new Squall();
        Stats zid = new Zidane();
        Stats tid = new Tidus();
        Stats yun = new Yuna();

        String versus = GetInput.getInput("\nWhich of these Characters would you like to go up against?");

        Stats ene1 = new TheEnemy();


        switch (versus) {
        case "Cloud":
            ene1.setName(Names.CLOUD);
            ene1.setHairColor(Stats.BLONDE);
            ene1.setWep(Weapons.BUSTER_SWORD);
            ene1.setSkill(clo.skill);
            ene1.setAp(clo.ap);
            ene1.setStr(clo.str);
            ene1.setMag(clo.mag);
            break;
        case "Squall":
            ene1.setName(Names.SQUALL);
            ene1.setHairColor(Stats.BLACK);
            ene1.setWep(Weapons.LIONHEART);
            ene1.setSkill(squ.skill);
            ene1.setAp(squ.ap);
            ene1.setStr(squ.str);
            ene1.setMag(squ.mag);
            break;
        case "Zidane":
            ene1.setName(Names.ZIDANE);
            ene1.setHairColor(Stats.LIGHTBROWN);
            ene1.setWep(Weapons.THIEF_DAGGER);
            ene1.setSkill(zid.skill);
            ene1.setAp(zid.ap);
            ene1.setStr(zid.str);
            ene1.setMag(zid.mag);
            break;
        case "Tidus":           
            ene1.setName(Names.TIDUS);
            ene1.setHairColor(Stats.BLONDE);
            ene1.setWep(Weapons.CALADBOLG);
            ene1.setSkill(tid.skill);
            ene1.setAp(tid.ap);
            ene1.setStr(tid.str);
            ene1.setMag(tid.mag);
            break;
        case "Yuna":
            ene1.setName(Names.YUNA);
            ene1.setHairColor(Stats.DARKBROWN);
            ene1.setWep(Weapons.NIRVANA);
            ene1.setSkill(yun.skill);
            ene1.setAp(yun.ap);
            ene1.setStr(yun.str);
            ene1.setMag(yun.mag);
            break;
        default:
            System.out.println("You did not enter a valid character name");
            break;


        } 
        System.out.println("You have chosen to face " + ene1.name);
        System.out.println("Enemy Skill = " + ene1.skill + " Enemy Weapon = " + ene1.wep);



        System.out.println("Enemy Skill = " + ene1.skill + " Enemy Weapon = " + ene1.wep);

        int eneTotal = ene1.skill + ene1.ap + ene1.str + ene1.mag;

        Stats player = new ThePlayer();

        String plN = GetInput.getInput("What is your name?");
        player.playerName = plN;        
        System.out.println("So Your name is " + player.playerName);

        String plWep = GetInput.getInput("What is your Weapon's name?");
        player.playerWep = plWep;
        System.out.println("So your Weapon is " + player.playerWep);

        Random generator = new Random();
        int plSkill = generator.nextInt(10);
        player.skill = plSkill;
        System.out.println("Your skill level is " + player.skill);

        Random gn = new Random();
        int plAp = gn.nextInt(10 - 5) + 5;
        System.out.println("So your Attack Power is " + plAp);
        player.ap =  plAp;

        Random gns = new Random();
        int plStr = gns.nextInt(10);
        System.out.println("So your Strength is " + plStr);
        plStr = player.str;

        Random gnm = new Random();
        int plMag = gnm.nextInt(10 - 5) + 5;
        player.mag = plMag;
        System.out.println("So your Magic is " + player.mag);


        int plHax = 15;
        double doubleResult = plHax;


        double ene1Hax = 3.99;
        int intResult = (int)ene1Hax;


        double doubleValue = 6.99;
        Double doubleObj = new Double(doubleValue);
        int intR = doubleObj.intValue();

        System.out.println(intR);

        int plyrTotal = player.skill + player.ap + player.str + player.mag;

        if (plyrTotal > eneTotal) {
            System.out.println("Congratulations you beat " + ene1.name + " Please Play Again!" );

        } else if (plyrTotal == eneTotal) {
            System.out.println("You drew with " + ene1.name + " Play again and Win!");
        }
        else
        {
            System.out.println("You were not succesful this time " + ene1.name + " Defeated you by " + (eneTotal - plyrTotal) + " Please Try Again");

        }


    }

}

Now after this I have a whole lot more code generating random numbers for the players "stats", and the character, and then matching the total values of their stats to determine "a winner" which I would like to put in a separate class. My issue is,

how do I get ene1 in a separate class with the values that are input in the switch statement in the Main class.

Updated to full main method just for clarity

BenMorel
  • 34,448
  • 50
  • 182
  • 322
FinalFind
  • 131
  • 1
  • 6
  • I would select the code you want to extract into another method using the refactoring in your IDE. Then I would move the method to another class (again using your IDE) – Peter Lawrey Jan 10 '13 at 17:47

4 Answers4

1

One of the issues you have is that you are assigning Enemy as a subclass of Stats, but with no relation to your characters. So while the enemy has the same attributes of a character, it has no relationship in which to speak to the character. Instead of copying all of these values like name and color, instead make Enemy it's own entity that holds a Stats value, that shares an interface with stats. The enemy can then use that interface to call various methods in the Stats class.

public class Static implements Actions{
    // ...
}

public class Enemy implements Actions{
   private Static characterType;
}

Also for future reference, could you please design your questions to be more generic? It not only helps those who don't understand the references, but also makes it easier for people who have similar problems to find this.

Zylth
  • 217
  • 1
  • 6
0

You can just pass it into a method (or constructor) of that class as you would any other variable or text. I.e.

 clo.setOpponent(ene1);

Then have the implementation of setOpponent be like this

 class Stats
 {
      private Stats opponent;
      //ALL OF YOUR OTHER CODE AND METHODS GO HERE

      public void setOpponent(Stats enemy)
      {
          opponent = enemy;
      }
 }

This doesn't take into account the poor OO design of your application, it only answers your question. Better design comes with more practice and some studying (look up IS-A and HAS-A relationships to help with basic OO design)

Steve's a D
  • 3,801
  • 10
  • 39
  • 60
  • So how should I have designed it to be more OO friendly? Wouldnt pretend to be an expert (or even close), I only really started learning java for an hour or so a night about a month ago – FinalFind Jan 10 '13 at 18:05
  • @FinalFind Zylth's answer covers a good approach but it might be confusing considering you're pretty new not only to OO but to programming in general. I can't teach you OO in a comment but check out this question+answer for a place to start: http://stackoverflow.com/questions/2218937/has-a-is-a-terminology-in-object-oriented-language – Steve's a D Jan 10 '13 at 18:09
  • Aye, that you cant, but if you have the time, could you inform me how you would have gone about this? Looking at the link on your post, I have not considered IS-A and HAS-A sufficiently. There was me sat thinking I had made a half decent Java app =P – FinalFind Jan 10 '13 at 18:22
  • doesn't this keep my code all in one class anyway as my code does work in its current state, It just feels wrong to me that the execution is all in one massive Main class (as seen in OP) – FinalFind Jan 10 '13 at 21:19
  • @FinalFind You don't want all of your code to execute in one class. You want to do a couple of things - you want to break the code up. Not only do you want to break it up but you want to put it into real life entities that make sense. Each class should have one purpose (single responsibility principle). This will help with preventing code duplication. So you have all of these classes named after specific fighters. These should be instance names not classes. What do all of these classes have in common? Everything. Name, hair color,etc. So they could all be the same class from an OOD perspective – Steve's a D Jan 11 '13 at 12:57
  • @FinalFind So the other answers give great ways to design this, but to get used to OOD you could just create one class called "Fighter". When the user selects who they want to play as you could use a switch statement to create an instance of the fighter they want. Then when the user asks for an opponent do the same thing (Could also use the Factory pattern but I think thats a little advanced for you). Fighter is a real life entity, you can find a fighter in the real world, no? You can't find a "Stats" entity in real life, or a "Cloud" or "Yuna" entity in real life... OOD takes a long – Steve's a D Jan 11 '13 at 13:00
  • @FinalFind time to master - something which I still haven't done. Its not something you can master from one or two or even ten applications. When you do get comfortable with it and come up with a design everbody who looks at it thinks their way is better. For projects in large corporations the design aspect of it can get prett heated between team members. There is no designs that are "right" but there are designs that are wrong – Steve's a D Jan 11 '13 at 13:02
  • @FinalFind considering that this answer is the only answer that *actually answers your question* could you select it as the correct answer? (Green check next to the question) – Steve's a D Jan 11 '13 at 13:06
  • Thanks for your information, assistance and guidance man, I will bear that in mind, and aye, I was unsure when making them all seperate classes if they should just be delcared as instances of stats (and stats should have been called characters for that matter), I have been doing some research into some OO fundamentals. What you are saying is that the Squall, Yuna, Cloud etc should all have been declared inside the stats class as instances of the stats class? Rather than constructor (methods?) in seperate clasees as subclasses of the stats class (which is how I defined them). – FinalFind Jan 11 '13 at 16:37
  • @FinalFind I'm saying that Squall, Yuna, Cloud, etc... is all data, thats the name of the fighters. They shouldn't be the name of any class. Think of a class as a cookie cutter, or a blue print of objects that you want to create. Then you use that cookie cutter to create cookies. Cookies can vary - some have chocolate chip, others oatmeal... but the overall shape is the same. The chocolate chips and oatmeal are the different stats VALUES that you put into the class (properties). Name, hair color, strength is the shape of your cookie cutter – Steve's a D Jan 11 '13 at 16:41
  • Nice analogy =), right, and in OO/java terms Cloud and Yuna, should be instances of the "Fighters" class. Am a tad confused because the tutorials I was following prior to this had the example of Olives, and each different olive type was defined in its own class and inherited from a superclass called Olive with the olives potential attributes described in a similar way to my application. But this might have just been to demonstrate inheritance idk – FinalFind Jan 11 '13 at 16:53
  • @FinalFind we can continue this discussion in chat http://chat.stackoverflow.com/rooms/139/java – Steve's a D Jan 11 '13 at 16:54
  • Well, fun as it is listening to Shotgun Ninja without being able to say anything, I very much see your point about the characters and their names not being subclasses, You are saying if I had People as a superclass I could then have subclasses called Politician, Sportsman etc then I would have their names and behaviours defined in their relevant subclasses. I do sound tech, but I have reason to learn some OO this year (all I have done previously is a limited amount of C and to be honest I want to, I enjoy the little programming I have done) Thanks for the Info, peace – FinalFind Jan 11 '13 at 17:17
0

In your particular example I would suggest to have Enumerations for the enemies, instead of using class hierarchy, as you have got the same fields for all enemy entities and you're not using any external storage here (like DB, file or whatever).

Class hierarchies are more suitable for situations, when you have several entities which have got both similarities and distinguishes. For example, you can have one class for regular enemies, and one class for 'enforced' enemies with some superskills, so the second class will inherit the first one and will have 1 additional field. This allows you to reduce the code duplicates and provides enough flexibility. Also, class hierarchy gives you some more benefits when you want to save this entities in some storage, for example, DB.

But in you case, it is more logical to use a single Enum to have some set of enemies, which is 'hardcoded' to the application. I would suggest the next solution:

public enum Enemy {
    CLOUD("Cloud", "Red", "Sword", 10),
    SQUALL("Cloud", "Black", "Minigun", 999)
    // and so on
    ;

    public String name;
    public String hairColor;
    public String wep;
    public int skill;

    Enemy(String name, String hairColor, String wep, int skill /* and so on */) {
        this.name = name;
        this.hairColor = hairColor;
        this.wep = wep;
        this.skill = skill;
    }

    public static Enemy getByName(String name) {
        for (Enemy enemy : Enemy.values()) {
            if (enemy.name.equalsIgnoreCase(name)) {
                return enemy;
            }
        }
        throw new IllegalArgumentException("Enemy with the name `" + name + "` doesn't exist.");
    }
}

And then it is possible to initialize an enemy in the main class by its key name:

String versus = GetInput.getInput("\nWhich of these Characters would you like to go up against?");
try {
    Enemy enemy = Enemy.getByName(versus);
} catch (IllegalArgumentException e) {  // processing incorrect input
    System.out.println(e.getMessage());
}

Then you can pass this constant to any other class to process it and calculate whatever you want:

public class FightCalculator {
    public int calculateScore(Enemy enemy) {
         // Some calculations here...
    }
}
Roman Reva
  • 55
  • 1
  • 7
  • Heh, I actually have an Enum with the names in it anyway. I didnt realise you could use them for anything other than calling as Strings – FinalFind Jan 10 '13 at 18:27
  • public enum Names { CLOUD("Cloud"), SQUALL("Squall"), ZIDANE("Zidane"), TIDUS("Tidus"), YUNA("Yuna"); private String nameAsString; private Names(String nameAsString) { this.nameAsString = nameAsString; } @Override public String toString() { return this.nameAsString; } } – FinalFind Jan 10 '13 at 18:28
  • Using the enums for storing names could be a nice solution too, but it seems that in your case you can have a bit more data in each enum item :) And you still can your Weapons and Stats enums as fields inside of Enemy enum. – Roman Reva Jan 10 '13 at 18:36
  • So you are saying to completely remove all the character classes i.e. Cloud, Squall etc (which look like this) `import com.George.revision.Characters; import com.George.revision.Weapons; public class Squall extends Stats { public Squall() { setLionheart(7); this.hairColor = Stats.BLACK; this.name = Characters.SQUALL; this.wep = Weapons.LIONHEART; this.skill = 7; this.ap = getLionheart(); this.mag = 4; this.str = 8; } }` And put them into a single Enum? – FinalFind Jan 10 '13 at 18:54
  • Or are you saying to make this Enum just for the Enemy? – FinalFind Jan 10 '13 at 19:02
  • The first. I suggest to remove classes Cloud, Squall, etc. and create them as constants inside the Enemy enum. This would make your OOP model more clear and readable and will work fine until you want to store entities in external storage :) So, if you want to add DB support later on - it's better to go with solution suggested by Steve and your current domain model. Otherwise, enums are good enough. – Roman Reva Jan 10 '13 at 19:50
0

It might help to create separate Hero and Enemy classes. Or if your characters aren't "good" or "bad" guys, perhaps just a Character class. For the following discussion, I will simply use Character, but you can replace this with Hero or Enemy and it should still make sense.

First of all, what is the relationship between a Character and a Stats. I think a Characater has a Stats. This means that Character should have a member variable of type Stats:

class Character {
    private Stats stats = new Stats();
}

Now you can add a constructor that a new Stats object for a Character. This means that the Character needs to take several parameters for each of the values stored in a Stats. One advantage of this approach is that you write the code to set up a Stats object just once and then each case statement has just 1 line:

ene1 = new Character(/* all the stats values go here */);

The moral of the story here is that when you find yourself repeating code, you should consider writing a method which encapsulates that repetition.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268