Andere Leute haben bereits meine ursprüngliche Idee, die Matrixmethode, vorgeschlagen, aber zusätzlich zur Konsolidierung der if-Anweisungen können Sie einige Ihrer Probleme vermeiden, indem Sie sicherstellen, dass die angegebenen Argumente im erwarteten Bereich liegen, und indem Sie direkte Rückgaben verwenden (einige Codierungen) Standards, bei denen ich gesehen habe, dass One-Point-of-Exit für Funktionen erzwungen wird, aber ich habe festgestellt, dass Mehrfachrückgaben sehr nützlich sind, um die Pfeilcodierung zu vermeiden, und angesichts der Verbreitung von Ausnahmen in Java macht es ohnehin nicht viel Sinn, eine solche Regel strikt durchzusetzen da jede nicht erfasste Ausnahme, die innerhalb der Methode ausgelöst wird, ohnehin ein möglicher Austrittspunkt ist). Das Verschachteln von switch-Anweisungen ist eine Möglichkeit, aber für den kleinen Wertebereich, den Sie hier überprüfen, finde ich, dass Anweisungen kompakter sind und wahrscheinlich nicht zu einem großen Leistungsunterschied führen.
public int fightMath(int one, int two) {
if (one > 3 || one < 0 || two > 3 || two < 0) {
throw new IllegalArgumentException("Result is undefined for arguments outside the range [0, 3]");
}
if (one <= 1) {
if (two <= 1) return 0;
if (two - one == 2) return 1;
return 2; // two can only be 3 here, no need for an explicit conditional
}
// one >= 2
if (two >= 2) return 3;
if (two == 1) return 1;
return 2; // two can only be 0 here
}
Dies ist letztendlich weniger lesbar als es sonst aufgrund der Unregelmäßigkeit von Teilen der Eingabe-> Ergebniszuordnung sein könnte. Ich bevorzuge den Matrixstil stattdessen aufgrund seiner Einfachheit und der Art und Weise, wie Sie die Matrix so einrichten können, dass sie visuell sinnvoll ist (obwohl dies teilweise durch meine Erinnerungen an Karnaugh-Karten beeinflusst wird):
int[][] results = {{0, 0, 1, 2},
{0, 0, 2, 1},
{2, 1, 3, 3},
{2, 1, 3, 3}};
Update: In Anbetracht Ihrer Erwähnung von Blockieren / Schlagen ist hier eine radikalere Änderung der Funktion, die aufgezählte Typen mit Eigenschaften / Attributen für Eingaben und das Ergebnis verwendet und das Ergebnis ein wenig ändert, um das Blockieren zu berücksichtigen, was zu mehr führen sollte lesbare Funktion.
enum MoveType {
ATTACK,
BLOCK;
}
enum MoveHeight {
HIGH,
LOW;
}
enum Move {
// Enum members can have properties/attributes/data members of their own
ATTACK_HIGH(MoveType.ATTACK, MoveHeight.HIGH),
ATTACK_LOW(MoveType.ATTACK, MoveHeight.LOW),
BLOCK_HIGH(MoveType.BLOCK, MoveHeight.HIGH),
BLOCK_LOW(MoveType.BLOCK, MoveHeight.LOW);
public final MoveType type;
public final MoveHeight height;
private Move(MoveType type, MoveHeight height) {
this.type = type;
this.height = height;
}
/** Makes the attack checks later on simpler. */
public boolean isAttack() {
return this.type == MoveType.ATTACK;
}
}
enum LandedHit {
NEITHER,
PLAYER_ONE,
PLAYER_TWO,
BOTH;
}
LandedHit fightMath(Move one, Move two) {
// One is an attack, the other is a block
if (one.type != two.type) {
// attack at some height gets blocked by block at same height
if (one.height == two.height) return LandedHit.NEITHER;
// Either player 1 attacked or player 2 attacked; whoever did
// lands a hit
if (one.isAttack()) return LandedHit.PLAYER_ONE;
return LandedHit.PLAYER_TWO;
}
// both attack
if (one.isAttack()) return LandedHit.BOTH;
// both block
return LandedHit.NEITHER;
}
Sie müssen nicht einmal die Funktion selbst ändern, wenn Sie Blöcke / Angriffe mit mehr Höhen hinzufügen möchten, nur die Aufzählungen. Das Hinzufügen zusätzlicher Arten von Zügen erfordert jedoch wahrscheinlich eine Änderung der Funktion. Außerdem ist EnumSet
s möglicherweise erweiterbarer als die Verwendung zusätzlicher Aufzählungen als Eigenschaften der Hauptaufzählung, z. B. EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);
und dann attacks.contains(move)
eher als move.type == MoveType.ATTACK
, obwohl die Verwendung von EnumSet
s wahrscheinlich etwas langsamer ist als direkte Gleichheitsprüfungen.
Für den Fall, dass ein erfolgreicher Block in einem Zähler ergibt, können Sie ersetzen if (one.height == two.height) return LandedHit.NEITHER;
mit
if (one.height == two.height) {
// Successful block results in a counter against the attacker
if (one.isAttack()) return LandedHit.PLAYER_TWO;
return LandedHit.PLAYER_ONE;
}
Das Ersetzen einiger if
Anweisungen durch die Verwendung des ternären Operators ( boolean_expression ? result_if_true : result_if_false
) könnte den Code kompakter machen (zum Beispiel würde der Code im vorhergehenden Block werden return one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;
), aber das kann zu schwer lesbaren Oneliner führen, so dass ich es nicht tun würde. Ich empfehle es für komplexere Verzweigungen.