javaclassswitch-statementdecoupling

Trouble decoupling in Java using switch statements


currently I am making a small type game, which in my player class I have far to many importants and dependencies.

So I have decided to separate the switch statements in its own individual class to be called in a method.

For context the player code

import Accessories.BasicSight;
import Accessories.BasicSilencer;
import Accessories.BasicStock;
import Accessories.GoodSight;
import Accessories.GoodSilencer;
import Accessories.GoodStock;
import Accessories.GreatSight;
import Accessories.GreatSilencer;
import Accessories.GreatStock;
import PlayerState.AliveState;
import PlayerState.PlayerState;
import Guns.Weapon;
import Guns.*;

import java.util.Scanner;

public class PlayerSingleton {

    private static PlayerSingleton player;
    Scanner scanner = new Scanner(System.in);

    private String playerName;
    private int lives;
    private Integer health;
    public static Integer MAX_PLAYER_HEALTH = 500;
    public static Integer DEFAULT_PLAYER_LIVES = 2;

    private PlayerState playerState;

    private Weapon weapon;

    private PlayerSingleton(Weapon weapon, String pName) {
        this.weapon = weapon;
        this.playerName = pName;
        this.lives = DEFAULT_PLAYER_LIVES;
        setState(new AliveState(this));
    }

    public static PlayerSingleton getInstance(String choice, String name) {
        System.out.println("Choose Weapon to play the with: ");

        Weapon weapon = PlayerSingleton.chooseWeapon(choice);

        weapon.getDescription();

        if (player == null) {
            player = new PlayerSingleton(weapon, name);
        }
        return player;
    }

    public void sufferDamage(int damage) {
        playerState.takeDamage(damage);
    }

    public void respawn() {
        playerState.respawn();
    }

    public int getLives() {
        return lives;
    }

    public void setLives(int lives) {
        this.lives = DEFAULT_PLAYER_LIVES;
    }

    public int getHealth() {
        return health;
    }

    public void setHealth(Integer health) {
        this.health = health;
    }

    public void setState(PlayerState playerState) {
        this.playerState = playerState;
    }

    public void chosenWeapon() {
        System.out.println("Player Info: " + playerName + " " + "Has: " + health + " health and " + lives + " lives");
        System.out.println(weapon.getDescription() + ":" + " base damage: " + weapon.damage());
        
    }

    public static Weapon chooseWeapon(String choice) {
        switch (choice) {
            case "MP5":
                System.out.println("You have chosen MP5!");
                return new MP5Weapon();
            case "SNIPER":
                System.out.println("You have chosen Sniper!");
                return new SniperRifleWeapon();
            case "SHOTGUN":
                System.out.println("You have chosen Shotgun!");
                return new ShotgunWeapon();
            default:
                System.out.println("No gun by that name found!");
                return null;
        }
        
    }  
     public void addBasicAttachment(String attachment) {

        switch (attachment) {
            case "SIGHT":
                weapon = new BasicSight(weapon);
                break;
            case "SILENCER":
                weapon = new BasicSilencer(weapon);
                break;
            case "STOCK":
                weapon = new BasicStock(weapon);
                break;
            default:
                System.out.println("No Attachment found!");
        }
    }

    public void getBasicAttachment(String attachment) {

    }

    public void addGoodAttachment(String attachment) {
        switch (attachment) {
            case "SIGHT":
                weapon = new GoodSight(weapon);
                break;
            case "SILENCER":
                weapon = new GoodSilencer(weapon);
                break;
            case "STOCK":
                weapon = new GoodStock(weapon);
                break;
            default:
                System.out.println("No Attachment found!");
        }
    }

    public void addGreatAttachment(String attachment) {
        switch (attachment) {
            case "SIGHT":
                weapon = new GreatSight(weapon);
                break;
            case "SILENCER":
                weapon = new GreatSilencer(weapon);
                break;
            case "STOCK":
                weapon = new GreatStock(weapon);
                break;
            default:
                System.out.println("No Attachment found!");
        }
    }
    
}

So the code above is my Player class, I would like to put the addBasic/Good/Great weapon attachments in another separate class file, to then allow better decoupled code. I am not sure how to do this with a switch statement.

Any ideas on how to approach this?

Edit:

The switch methods are being called form the main:

import Accessories.*;
import Player.PlayerSingleton;
import Guns.Weapon;
import Guns.MP5Weapon;
import java.util.Scanner;

public class FireingRange {
Weapon weapon;
    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        
        String name = scanner.nextLine();
      
        System.out.println(name + " to begin the game, first type a weapon you would like to play with:");
        System.out.println();
        System.out.println("Choose Weapon to play the with: ");
        System.out.println("'Shotgun' for Shotgun");
        System.out.println("'Sniper' for Sniper");
        System.out.println("'MP5' for MP5");
        String chooseWeapon = scanner.next().toUpperCase();

        PlayerSingleton player = PlayerSingleton.getInstance(chooseWeapon, name);
       
        System.out.println(player.getHealth());
        System.out.println(player.getLives());

       
        System.out.println();
      
        player.chosenWeapon();
        System.out.println("Are you ready to play the game?");

    }

The Abstract accessory class:

import Guns.Weapon;

public abstract class AccessoryDecorator extends Weapon{
    public abstract String getDescription();
}

An accessory extending AccessoryDecorator:

public class BasicSight extends AccessoryDecorator {
    Weapon weapon;
    
    public BasicSight(Weapon weapon) {
        this.weapon = weapon;
    }
    @Override
    public String getDescription() {
        return weapon.getDescription() + ", Basic Sight";
    } 
    @Override
    public double weight() {
        return 7 + weapon.weight();
    }

    
    @Override
    public double damage() {
        return 7 + weapon.damage();
   }

So the objective is to have:

  public void addBasicAttachment(String attachment) {

        switch (attachment) {
            case "SIGHT":
                weapon = new BasicSight(weapon);
                break;
            case "SILENCER":
                weapon = new BasicSilencer(weapon);
                break;
            case "STOCK":
                weapon = new BasicStock(weapon);
                break;
            default:
                System.out.println("No Attachment found!");
        }
    }

    public void addGoodAttachment(String attachment) {
        switch (attachment) {
            case "SIGHT":
                weapon = new GoodSight(weapon);
                break;
            case "SILENCER":
                weapon = new GoodSilencer(weapon);
                break;
            case "STOCK":
                weapon = new GoodStock(weapon);
                break;
            default:
                System.out.println("No Attachment found!");
        }
    }
    public void addGreatAttachment(String attachment) {
        switch (attachment) {
            case "SIGHT":
                weapon = new GreatSight(weapon);
                break;
            case "SILENCER":
                weapon = new GreatSilencer(weapon);
                break;
            case "STOCK":
                weapon = new GreatStock(weapon);
                break;
            default:
                System.out.println("No Attachment found!");
        }
    }

In a class of its own, to then be able to call the switch with result, which will then add to the values of the gun selected.

I cant seem to think how to return the accessory depending on what accessory is created.


Solution

  • You could make a base class with some functionality and then add code by the means of extension. Another option is making a utility class with only static functions that you can call to manipulate the passed object, like this:

    public class PlayerUtil {
        public static void addGreatAttachment(PlayerSingleton player, String attachment) {
            Weapon weapon;
            switch (attachment) {
                case "SIGHT":
                    weapon = new GreatSight(weapon);
                    break;
                case "SILENCER":
                    weapon = new GreatSilencer(weapon);
                    break;
                case "STOCK":
                    weapon = new GreatStock(weapon);
                    break;
                default:
                    System.out.println("No Attachment found!");
            }
            player.setWeapon(weapon);
        }
    }
    

    But in your case I recommend you create another class: "EquipedWeapon". This class will have its own Weapon field and possibly multiple Attachment fields. You can put the code for changing attachments in there, and even add some methods for getting stats about the weapon with the attachments on it, like the damage or bullet spread or wtv, then create a getter for the weapon inside of PlayerSingleton.

    I'd also recommend creating an AttachmentManager class in your game that holds all attachments and allows you to select them using various methods. Or you could make all attachments extend one Attachment class that holds a static list of all attachments. If you add the name as a field inside the attachment class, you can then create a static method that iterates over all attachments and finds one with the right name and returns it. Or use a static Hashmap<String, Attachment>.