D'autres personnes ont déjà suggéré mon idée initiale, la méthode matricielle, mais en plus de consolider les instructions if, vous pouvez éviter une partie de ce que vous avez en vous assurant que les arguments fournis sont dans la plage attendue et en utilisant des retours sur place (certains codages les normes que j'ai vues appliquent un point de sortie unique pour les fonctions, mais j'ai trouvé que les retours multiples sont très utiles pour éviter le codage des flèches et avec la prévalence d'exceptions en Java, il n'y a pas grand chose à faire appliquer strictement une telle règle de toute façon car toute exception non interceptée levée à l'intérieur de la méthode est un point de sortie possible de toute façon). L'imbrication des instructions de commutateur est une possibilité, mais pour la petite plage de valeurs que vous vérifiez ici, je trouve que les instructions doivent être plus compactes et ne devraient pas entraîner une grande différence de performances,
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
}
Cela finit par être moins lisible qu'il ne le serait autrement en raison de l'irrégularité de certaines parties du mappage d'entrée-> résultat. Je préfère le style de matrice en raison de sa simplicité et de la façon dont vous pouvez configurer la matrice pour qu'elle ait un sens visuel (bien que cela soit en partie influencé par mes souvenirs des cartes de Karnaugh):
int[][] results = {{0, 0, 1, 2},
{0, 0, 2, 1},
{2, 1, 3, 3},
{2, 1, 3, 3}};
Mise à jour: compte tenu de votre mention de blocage / frappe, voici un changement plus radical de la fonction qui utilise les types énumérés propriété / propriété d'attribut pour les entrées et le résultat et modifie également un peu le résultat pour tenir compte du blocage, ce qui devrait entraîner une plus fonction lisible.
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;
}
Vous n'avez même pas besoin de changer la fonction elle-même si vous voulez ajouter des blocs / attaques de plus hauteurs, juste les énumérations; cependant, l'ajout de types de mouvements supplémentaires nécessitera probablement une modification de la fonction. De plus, EnumSet
s peut être plus extensible que d'utiliser des énumérations supplémentaires en tant que propriétés de l'énumération principale, par exemple EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);
et puis attacks.contains(move)
plutôt que move.type == MoveType.ATTACK
, bien que l'utilisation de EnumSet
s soit probablement un peu plus lente que les vérifications égales directes.
Dans le cas où un blocage réussi aboutit à un compteur, vous pouvez remplacer if (one.height == two.height) return LandedHit.NEITHER;
par
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;
}
En outre, le remplacement de certaines des if
instructions par l'utilisation de l'opérateur ternaire ( boolean_expression ? result_if_true : result_if_false
) pourrait rendre le code plus compact (par exemple, le code du bloc précédent deviendrait return one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;
), mais cela peut conduire à des oneliners plus difficiles à lire, donc je ne le ferais pas '' Je le recommande pour des branchements plus complexes.