Другие люди уже предложили мою первоначальную идею, матричный метод, но в дополнение к консолидации операторов if вы можете избежать того, что у вас есть, убедившись, что предоставленные аргументы находятся в ожидаемом диапазоне и используя возвраты на месте (некоторое кодирование стандарты, которые я видел, применяют единую точку выхода для функций, но я обнаружил, что множественные возвраты очень полезны для избежания кодирования стрелок, и с преобладанием исключений в Java нет особого смысла строго соблюдать такое правило в любом случае поскольку любое неперехваченное исключение, выброшенное внутри метода, в любом случае является возможной точкой выхода). Возможно вложение операторов switch, но для небольшого диапазона значений, которые вы здесь проверяете, я нахожу, что операторы будут более компактными и вряд ли приведут к значительной разнице в производительности,
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
}
Это оказывается менее читабельным, чем могло бы быть из-за неправильности частей отображения ввода-> результата. Вместо этого я предпочитаю стиль матрицы из-за его простоты и того, как вы можете настроить матрицу так, чтобы она имела смысл визуально (хотя это частично зависит от моих воспоминаний о картах Карно):
int[][] results = {{0, 0, 1, 2},
{0, 0, 2, 1},
{2, 1, 3, 3},
{2, 1, 3, 3}};
Обновление: учитывая ваше упоминание о блокировке / ударе, вот более радикальное изменение в функции, которая использует перечисляемые типы, имеющие свойства / атрибуты, для входных данных и результата, а также немного изменяет результат для учета блокирования, что должно привести к более читаемая функция.
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;
}
Вам даже не нужно менять саму функцию, если вы хотите добавить блоки / атаки большей высоты, только перечисления; добавление дополнительных типов ходов, вероятно, потребует модификации функции. Кроме того, EnumSet
s может быть более расширяемым, чем использование дополнительных перечислений в качестве свойств основного перечисления, например, EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);
и тогда, attacks.contains(move)
а не move.type == MoveType.ATTACK
, хотя использование EnumSet
s, вероятно, будет немного медленнее, чем прямые проверки на равенство.
Для случая, когда успешный блок приводит к счетчику, вы можете заменить if (one.height == two.height) return LandedHit.NEITHER;
на
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;
}
Кроме того, замена некоторых if
операторов с использованием тернарного оператора ( boolean_expression ? result_if_true : result_if_false
) может сделать код более компактным (например, код в предыдущем блоке станет return one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;
), но это может привести к затруднению чтения однострочников, поэтому я бы не стал Я рекомендую его для более сложных ветвлений.