Інші люди вже запропонували мою початкову ідею, метод матриці, але на додаток до консолідації висловлювань if ви можете уникнути деякого з того, що у вас є, переконавшись, що подані аргументи знаходяться в очікуваному діапазоні та використовуючи на місці повернення (деяке кодування Стандарти Я бачив, як застосовується функція "одна точка виходу" для функцій, але я виявив, що багаторазове повернення дуже корисно для уникнення кодування стрілки і за поширеності винятків на Java, все одно не дуже важливо суворо виконувати таке правило так як будь-який невловимий виняток, кинутий всередині методу, все одно є можливою точкою виходу). Оператори введення комутаторів - це можливість, але для невеликого діапазону значень, які ви перевіряєте тут, я виявляю, чи твердження більш компактні та не можуть призвести до значної різниці в продуктивності,
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;
), але це може призвести до більш важкого для читання oneliners, щоб я не хотів ' t рекомендувати його для більш складних розгалужень.