Pong Khan
Pong Khan

Reputation: 11

Method implementation

I am quite new to java and need help in setting up the implementation of the method in a more sophisticated way - I have written this, but was told I need to use one of the GoF patterns in order to beautify it. Some help as to what I need to do would be most appropriated

protected void setWeapon() {
        if (this.getClass().getSimpleName().equals("FighterJet")) {
            this.weapon = new GuidedMissileSystem();
            System.out.println("FighterJet " + id + " equipped with " + weapon.getClass().getSimpleName());
        } else if (this.getClass().getSimpleName().equals("AttackHelicopter")) {
            this.weapon = new GrenadeLauncher();
            System.out.println("AttackHelicopter " + id + " equipped with " + weapon.getClass().getSimpleName());
        } else if (this.getClass().getSimpleName().equals("Tank")) {
            this.weapon = new Cannon();
            System.out.println("Tank " + id + " equipped with " + weapon.getClass().getSimpleName());
        } else if (this.getClass().getSimpleName().equals("InfantryMobilityVehicle")) {
            this.weapon = new MachineGun();
            System.out.println("InfantryMobilityVehicle " + id + " equipped with " + weapon.getClass().getSimpleName());
        } else if (this.getClass().getSimpleName().equals("Warship")) {
            this.weapon = new RocketLauncher();
            System.out.println("Warship " + id + " equipped with " + weapon.getClass().getSimpleName());
        } else if (this.getClass().getSimpleName().equals("Submarine")) {
            this.weapon = new TorpedoTube();
            System.out.println("Submarine " + id + " equipped with " + weapon.getClass().getSimpleName());
        }
    }

Upvotes: 0

Views: 73

Answers (2)

Henrique Barcelos
Henrique Barcelos

Reputation: 7900

@AndyTurner's answer can be illustrated by the following diagram:

Solution Diagram

Although this solution works, I disagree that it follows the Dependency Inversion principle, since the concrete classes are responsible for creating the Weapon objects. Furthermore, I belive it violates the Open-Closed Principle. Imagine that your Weapon is crashed during a battle. You couldn't just replace it for another.

I like to propose another solution, that is structurally similar, but functionally different:

Solution 2 Diagram

interface WarVehicle {
    boolean atack(WarVehicle other);
    boolean defend(WarVehicle from);
    void setWeapon(WarVehicle from);
    Weapon getWeapon();
    boolean acceptsWeapon();
}

abstract class AbstractWarVehicle {
    private Weapon weapon;
    public AbstractWarVehicle(Weapon weapon) {
        setWeapon(weapon);
    }

    public final void setWeapon(Weapon weapon) {
        if (!acceptsWeapon(weapon)) {
            throw new IllegalArgumentException("Weapon of type " 
                + weapon.getClass().getName() 
                + " cannot be added to WarVehicle of type " 
                + this.getClass().getName());
        }
        this.weapon = weapon;
    }

    public final Weapon getWeapon() {
        return this.weapon;
    }

    public boolean attack(WarVehicle other) {
        if (other != this) {
           return !other.defend(this);
        }
    }

    public boolean defend(WarVehicle from) {
        return this.getWeapon().getDefensePower() >= 
               from.getWeapon().getFirePower();
    }
}

class FighterJet extends AbstractWarVehicle {
    public boolean acceptsWeapon(Weapon weapon) {
        return weapon instanceof GuidedMissilesystem;
    }
}

class AttackHelicopter extends AbstractWarVehicle {
    public boolean acceptsWeapon(Weapon weapon) {
        return weapon instanceof GrenadeLauncher;
    }
}

// And so on...

Using it:

Weapon jet = new FighterJet(new GuidedMissleSystem()); // OK
Weapon jet2 = new FighterJet(new Cannon()); // throws!

Advantages of this approach:

  • It's a DIP implementation.
  • It's possible to just replace a Weapon with a similar one by using setWeapon.
  • It respects (to a certain extent) the OCP, as it's possible to subclass/compose Weapons. A FighterJet will accept anything that is an instance of GuidedMissileSystem, which means if your game is expanded and you get a SuperUltraMegaBlasterGeorgeForemanMissileSystem, you will still be able to use it if it inherits from GuidedMissileSystem.

Drawbacks of this approach:

  • The rules in acceptsWeapon are static, so they can't be changed on runtime.

Upvotes: 1

Andy Turner
Andy Turner

Reputation: 140328

Since this appears to be based on which subclass the instance is concretely, you could simply set a field in the constructor.

For instance:

class ParentClass {
  final Weapon weapon;

  ParentClass(Weapon weapon) {
    this.weapon = weapon;
  }
}

Then, in the Warship class:

class Warship extends ParentClass {
  Warship() {
    super(new RocketLauncher());
  }
}

(Visibility modifiers and other fields omitted for clarity)

Mind you, dependency injection isn't a GoF pattern, I don't think. It's just the cleanest one to use here.

Upvotes: 1

Related Questions