Interfaces to allow unit's ability to attack g

2019-07-18 12:24发布

问题:

Im making a text game that has an abstract Fighter class. within Fighter I have a variable called protected boolean isGround. then I have 2 abstract classes that extend Fighter called AirFighter and GroundFighter that basically set that isGround to be True or False, depending on if they are ground units or not.

I then want to set up the game so that in my battle phase the game checks if player 1's speed is faster then player 2, and depending on who is faster to then do like p1.attack(p2), and print out the stats of that battle, like the remaining health of each player or something like that.

I want a way for some airFighters to attack air, or attack air and ground, and I want some groundFighters to attack ground only, or ground and air. How would I go about doing this while staying loosely coupled? It is for a design pattern's class and its frustrating me. Is there a design pattern to help me with this?

Think of the game starcraft, that's basically what my game is mimicing, certain units like a marine can attack ground and air, some air units can only attack air, some can do both. I want to be able to do this without having some nasty if else checking statement.

回答1:

An easy way to do this is to have a method in the base Fighter that can be used to check if that Fighter can attack another:

public abstract class Fighter {
    public abstract boolean isGround ();
    public abstract boolean canAttack (Fighter target);
}

Subclasses can then override canAttack and make an appropriate decision. For example:

public class SurfaceToAirLauncher extends Fighter {
    @Override public boolean isGround () {
        // missile launcher is on the ground
        return true; 
    }
    @Override public boolean canAttack (Fighter target) {
        // we can only attack air targets
        return (target != null && !target.isGround());
    }
}

public class Dragon extends Fighter {
    @Override public boolean isGround () {
        // dragons are in the air
        return false; 
    }
    @Override public boolean canAttack (Fighter target) {
        // dragons can attack every target.
        return (target != null);
    }
}

public class Knight extends Fighter {
    @Override public boolean isGround () {
        // knights are on the ground
        return true; 
    }
    @Override public boolean canAttack (Fighter target) {
        // knights can only attack dragons
        return (target != null && target instanceof Dragon);
    }
}

You could remove the check for null if you know target will never be null. I included it for completeness.



回答2:

I'd suggest the Decorator pattern. You have a SimpleFighter as your base class (which has an canAttack(Fighter enemy) method that just returns false), and a FighterDecorator, both that implement the Fighter interface. See how to set up the constructor and delegating methods here:

http://en.wikipedia.org/wiki/Decorator_pattern

Then you have two classes that extend the FighterDecorator:

The GroundKillerDecorator that has a canAttack() method that says:

if(enemy.isGround) return true; else return super.canAttack();

The AirKillerDecorator that has a canAttack() method that says:

if(!enemy.isGround) return true; else return super.canAttack();

...

And that should let you compose your fighters like

new GroundKillerDecorator( new AirKillerDecorator( new SimpleFighter() ))

Optimally, you could replace SimpleFighter with two other classes, AirFighter and GroundFighter, that implement different base behavior (rather than not being able to attack anything by default), but still implement the same Fighter interface.