지뢰 찾기 폭탄
주변의 모든 사각형을 확인하고 주변의 폭탄 수를 반환하는 메서드가 있습니다. 하지만 정말 길고보기 흉한 코드인데 짧게 할 수 있을까요?
final int MINE =10
for (int x = 0; x < counts.length; x++) {
for (int y = 0; y < counts[0].length; y++) {
if (counts[x][y] != MINE) {
int Minesearch = 0;
if (x > 0 && y > 0 && counts[x-1][y-1] == MINE) {//up left
Minesearch++;
}
if (y > 0 && counts[x][y-1] == MINE) {//up
Minesearch++;
}
if (x < counts.length - 1 && y > 0 && counts[x+1][y-1] == MINE) {//up right
Minesearch++;
}
if (x > 0 && counts[x-1][y] == MINE) {//left
Minesearch++;
}
if (x < counts.length - 1 && counts[x+1][y] == MINE) {//right
Minesearch++;
}
if (x > 0 && y < counts[0].length - 1 && counts[x-1][y+1] == MINE) {//down left
Minesearch++;
}
if (y < counts[0].length - 1 && counts[x][y+1] == MINE) {//down
Minesearch++;
}
if (x < counts.length - 1 && y < counts[0].length - 1 && counts[x+1][y+1] == MINE) {//down right
Minesearch++;
}
counts[x][y] = Minesearch;
}
}
}
}
답변
경계를 cyhecking하고 내부 메서드로 검사하여 리팩토링하여 단축하고 "아름답게"만들 수 있습니다.
private boolean isWithinBounds(int x, int y) {
return x >= 0 && y >= 0 && x < width && y < height;
}
private boolean isMine(int x, int y) {
return field[x][y] == MINE;
}
그런 다음 지뢰 수는 사소 해집니다 (3x3 사각형의 중심에 지뢰가 없다고 가정 할 수 있습니다. 그렇지 않으면 플레이어가 폭발하여 게임을 종료했을 것입니다) :
for (int x1 = x - 1; x1 <= x + 1; x1++) {
for (int y1 = y - 1; y1 <= y + 1; y1++) {
if (isWithinBounds(x1, y1) && isMine(x1, y1) {
mineCount++;
}
}
}
우리가 한 일은 코드를 메서드로 나누는 것입니다. 각각의 메서드는 작고 잘 정의 된 함수를 구현합니다. 각 방법은 정확히 한 가지를 수행하기 때문에 이해, 유지 관리 및 테스트가 더 쉬워집니다.
Java 이름 지정 규칙을 확인해야합니다 . 변수 이름은 camelCase, startingWithSmallLetter.
변수 및 메소드 이름은 코드가 존재하는 이유를 설명해야합니다. 예 mineSearch를 들어 변수가 광산을 검색하지 않기 때문에 혼란 스럽습니다. 따라서 mineCount더 나은 대안입니다.
CountsMINE분명히 광산을 포함하는 셀에 대한 표식이지만 주변 광산 수를 포함하는 값이 포함되어 있기 때문에 혼란 스럽습니다 . 지뢰 찾기 클론 (또는 실제로 지뢰 찾기 솔버)을 한 번 수행했고 Cell-objects를 포함하는 배열을 사용했습니다. Cell 객체는 셀의 상태 (스텝 온, 플래그 지정, 알 수 없음) 및 밟은 주변 광산 수를 쿼리하는 메서드를 제공했습니다.
@TorbenPutkonen의 답변은 정확하지만 문제에 대한 절차 적 접근 방식입니다.
절차 적 접근 방식에는 문제가 없지만 Java는 객체 지향 언어 이기 때문에 대신 OO 접근 방식을 찾아 볼 수 있습니다.
이웃 수표를 다음 enum과 같이 추출합니다 .
enum Direction {
NORTH{
boolean isBomb(inx x, int y, boolean[] field){
if(0 < x)
return BOMB == field(x-1, y);
else
return false;
}
},
NORTH_WEST{
boolean isBomb(inx x, int y, boolean[] field){
if(0 < x && 0 < y)
return BOMB == field(x-1, y-1);
else
return false;
}
},
SOUTH{
boolean isBomb(inx x, int y, boolean[] field){
if(field.length-1 > x)
return BOMB == field(x+1, y);
else
return false;
}
},
SOUTH_EAST{
boolean isBomb(inx x, int y, boolean[] field){
if(field.length-1 > x && field[0].length-1>y)
return BOMB == field(x+1, y+1);
else
return false;
}
}
// other directions following same pattern
abstract boolean isBomb(inx x, int y, boolean[] field);
}
이점은이 열거 형이 자체 파일에있을 수 있고 매우 제한된 책임을 가질 수 있다는 것입니다. 그것은 무엇을 하는지를 이해하기 쉽다는 것을 의미합니다.
계산 방법에서 다음 enum과 같이 상수를 간단히 반복 할 수 있습니다 .
for (int x = 0; x < counts.length; x++) {
for (int y = 0; y < counts[0].length; y++) {
int mineCount =0;
for(Direction direction : Direction.values()) {
if (direction.isBomb(x, y, counts) ) {
mineCount++;
}
}
}
}
다음 단계로 메서드 서명을 변경하여 "말하고 묻지 마십시오"원칙을 적용합니다.
abstract int getBombValueOf(inx x, int y, boolean[] field);
의 구현 enum은 다음과 같이 변경됩니다.
int getBombValueOf(inx x, int y, boolean[] field){
if(0 < x && BOMB == field(x-1, y))
return 1;
else
return 0;
},
"elvis 연산자"로 단순화 할 수 있습니다.
int getBombValueOf(inx x, int y, boolean[] field){
return (0 < x && BOMB == field(x-1, y))
? 1
: 0;
},
사용법은 다음과 같이 변경됩니다.
for (int x = 0; x < counts.length; x++) {
for (int y = 0; y < counts[0].length; y++) {
int mineCount =0;
for(Direction direction : Direction.values()) {
mineCount +=
direction.getBombValueOf(x, y, counts) );
}
}
}
a FunctionalInterface및 간단한 컬렉션 을 사용하여 동일한 작업을 수행 할 수 있습니다 (이웃 계산을 다른 파일로 이동하는 경우 제외) .
@FunctionalInterface
interface Direction{
int getBombValueOf(inx x, int y, boolean[] field);
}
private final Collection<Direction> directions = new HashSet<>();
// in constructor
directions.add(new Direction() { // old style anonymous inner class
int getBombValueOf(inx x, int y, boolean[] field){
return (0 < x && BOMB == field(x-1, y))
? 1
: 0;
}
};
directions.add((x, y, field)-> { // Java8 Lambda
return (0 < x && 0 < y &&BOMB == field(x-1, y-1))
? 1
: 0;
};
// more following same pattern
// in your method
for (int x = 0; x < counts.length; x++) {
for (int y = 0; y < counts[0].length; y++) {
int mineCount =0;
for(Direction direction : directions) {
mineCount +=
direction.getBombValueOf(x, y, counts) );
}
}
}
게임 필드가 프리미티브 의 배열이 아니라 Objects 의 컬렉션 이면 OO 원칙에서 훨씬 더 많은 이점을 얻을 수 있습니다 . 그러나 그것은 다른 대답에 대한 물건 일 수 있습니다 ...; o)