Những người khác đã đề xuất ý tưởng ban đầu của tôi, phương thức ma trận, nhưng ngoài việc hợp nhất các câu lệnh if, bạn có thể tránh một số điều bạn có bằng cách đảm bảo các đối số được cung cấp nằm trong phạm vi dự kiến và bằng cách sử dụng trả về tại chỗ (một số mã hóa các tiêu chuẩn mà tôi đã thấy thực thi một điểm thoát cho các hàm, nhưng tôi thấy rằng nhiều trả về rất hữu ích để tránh mã hóa mũi tên và với sự phổ biến của các ngoại lệ trong Java dù sao cũng không thực thi nghiêm ngặt quy tắc như vậy vì bất kỳ ngoại lệ chưa được lưu nào được ném bên trong phương thức là một điểm có thể thoát ra bằng mọi cách). Các câu lệnh chuyển đổi lồng nhau là một khả năng, nhưng đối với phạm vi giá trị nhỏ mà bạn đang kiểm tra ở đây tôi thấy nếu các câu lệnh nhỏ gọn hơn và không có khả năng dẫn đến sự khác biệt về hiệu suất,
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
}
Điều này cuối cùng không thể đọc được hơn nó có thể là do sự bất thường của các phần của ánh xạ kết quả đầu vào->. Thay vào đó, tôi thích phong cách ma trận do tính đơn giản của nó và cách bạn có thể thiết lập ma trận để có ý nghĩa trực quan (mặc dù điều đó một phần bị ảnh hưởng bởi ký ức của tôi về bản đồ Karnaugh):
int[][] results = {{0, 0, 1, 2},
{0, 0, 2, 1},
{2, 1, 3, 3},
{2, 1, 3, 3}};
Cập nhật: Khi bạn đề cập đến việc chặn / đánh, đây là một thay đổi căn bản hơn đối với chức năng sử dụng các kiểu liệt kê được giữ đúng / thuộc tính cho các đầu vào và kết quả và cũng sửa đổi kết quả một chút để tính đến việc chặn, điều này sẽ dẫn đến nhiều hơn chức năng có thể đọc được.
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;
}
Bạn thậm chí không phải thay đổi chức năng nếu bạn muốn thêm các khối / tấn công có độ cao hơn, chỉ là các enum; thêm các loại di chuyển có thể sẽ yêu cầu sửa đổi chức năng, mặc dù. Ngoài ra, EnumSet
s có thể mở rộng hơn so với sử dụng enum bổ sung làm thuộc tính của enum chính, ví dụ EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);
và sau đó attacks.contains(move)
hơn là move.type == MoveType.ATTACK
, mặc dù sử dụng EnumSet
s có thể sẽ chậm hơn một chút so với kiểm tra bằng trực tiếp.
Đối với trường hợp khối thành công dẫn đến bộ đếm, bạn có thể thay thế if (one.height == two.height) return LandedHit.NEITHER;
bằng
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;
}
Ngoài ra, việc thay thế một số if
câu lệnh bằng cách sử dụng toán tử ternary ( boolean_expression ? result_if_true : result_if_false
) có thể làm cho mã nhỏ gọn hơn (ví dụ: mã trong khối trước sẽ trở thành return one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;
), nhưng điều đó có thể dẫn đến các onelin khó đọc hơn nên tôi sẽ không đọc được t đề nghị nó cho phân nhánh phức tạp hơn.