https://github.com/IT-DO/Hangman
Ревью от Алексея @Raketa4000az
[Anton K]
Простая реализация в процедурном стиле, выполнена в одном классе.
Недостатки реализации:
- Команды работают неправильно
Хотите сыграть в игру "Виселица" . . . ?
Для начала игры введите: 1 + Enter
Для выхода из игры введите: 0 + Enter
123
Приветствуем Вас в игре Виселица. Цель игры: угадать слово, вводя по одной букве за шаг.
- На старте не видно виселицы
- Вместо одиночной буквы можно ввести целое слово и игра ее примет
- Вместо буквы можно ввести цифру 1 и игра ее примет
Буквы, которые вы уже ввели: [1]
- Одну и ту же букву можно ввести несколько раз и игра ее примет, а потом даже правильную букву посчитает неправильной
Введите предполагаемую букву и нажмите Enter.
ф
Есть такая буква!
<тут картинка виселицы>
Загаданное слово: Ф***
Количество оставшихся попыток: 5
Буквы, которые вы уже ввели: [Ф]
Введите предполагаемую букву и нажмите Enter.
Ф
Мимо! Попробуйте еще...
<тут картинка виселицы>
Загаданное слово: Ф***
Количество оставшихся попыток: 4
Буквы, которые вы уже ввели: [Ф, Ф]
- Совершенно ацкое пустое пространство в середине строки
Введите предполагаемую букву и нажмите Enter. Подсказка: Если хотите начать новый раунд, введите 0 + Enter
- При проигрыше в конце ресует пустую виселицу
Буквы, которые вы уже ввели: [А, А, Б, Ф, Ф]
Вы проиграли! Допущено слишком много ошибок.
Загаданное слово: ФЛЕШ
_____________
| |
|
|
|
|
‾‾‾‾‾‾‾‾‾‾‾‾‾
Хотите сыграть в игру "Виселица" . . . ?
Хорошо:
- Игра запускается
- Есть список ранее введенных букв. Да, работает неправильно, но он есть
- Возможность досрочно выйти из раунда
- Нейминг
- Широкое использование констант
- Простой понятный алгоритм
Замечания:
- Нейминг -Это не массив
ArrayList<String> array = new ArrayList<>();
-Название вводит в заблуждение. Метод не проверяет состояние, он его сообщает
String checkGameState(int mistakes)
--
Oracle Java code conventions, part."Naming conventions"
Мартин, "Чистый код", гл.2
--
- Комментарии -Коментарии тут в основном не несут полезной нагрузки, а констатируют очевидное
//точка входа в приложение
public static void main(String[] args)
//вывод виселицы в консоль
static void printHangman(int mistakes)
//пользовательское меню
static void menu()
Когда в проекте много каментов, это плохо- пользы от них практически нет, они только забивают пространство и мешают читать код. В идеале, комментариев вообще не должно быть, код должен объяснять сам себя через правильный нейминг и лаконичный код.
-Комментарии, которые можно вообще не заметить. Один из них находится на расстоянии 14 сантиметров правее строки, которую комментирует. Какая цель существования этого комментария- чтобы его увидели или нет?
---
Мартин, "ЧК", гл.4
---
-
Нарушение инкапсуляции, здесь ее у методов просто нет. Всегда явно указывай область видимости. В этой реализации публичным должен только метод main().
-
Нарушение DRY, магические буквы, числа, слова. Вводи константы
if (input == '1') {
//...
} else if (input == '0') {
//...
}
//ПРАВИЛЬНО:
private final static char PLAY = '1';
private final static char QUIT = '0';
private static final String MAIN_MENU_QUESTION = "Хотите сыграть в игру \"Виселица\" . . . ?\nДля начала игры введите: %c + Enter\nДля выхода из игры введите: %c + Enter"
.formatted(PLAY, QUIT);
if (input == PLAY) {
//...
} else if (input == QUIT) {
//...
}
--
Фаулер, "Рефакторинг", гл.8 п."Замена магического числа символической константой"
refactoring.guru "Замена магического числа символьной константой"
--
- Если в блоке if есть return(break, continue, throw, exit и т.д.), то else не пишется - это не имеет смысла
if (inputValidation(c)) {
return c;
} else {
System.out.println("Проверьте язык ввода, у нас только русские слова! ;)");
//...
}
//ПРАВИЛЬНО:
if (inputValidation(c)) {
return c;
}
System.out.println("Проверьте язык ввода, у нас только русские слова! ;)");
- Используй классы через их интерфейсы
ArrayList<String> createDictionary() {...}
ArrayList<String> array = new ArrayList<>();
//ПРАВИЛЬНО:
List<String> createDictionary() {...}
List<String> array = new ArrayList<>();
- Рекурсия должна использоваться только там, где она оптимальна алгоритмически. Например при обходе дерева и как его частный случай- нахождения пути в Симуляции в BFS. В других ситуациях, где рекурсия может быть замененена циклом, она должна быть им замененена. Рекурсия тяжело читается и при определенных условиях может привести к переполнению стека
static void menu() {
//...
menu();
//...
}
Убери рекурсию, замени ее использование на цикл while()
- Если нужно печатать текст с более, чем одним подстановочным значением или значение вставляется внутрь сообщения, используй форматированный вывод - тогда ты сразу будешь видеть шаблон.
System.out.println("Загаданное слово: " + maskWord.toUpperCase() + "\nКоличество оставшихся попыток: "
+ mistakes + "\nБуквы, которые вы уже ввели: " + userLetterSet.toString().toUpperCase() + "\n");
//ПРАВИЛЬНО:
System.out.printf("Загаданное слово: %s \nКоличество оставшихся попыток: %d \nБуквы, которые вы уже ввели: %s \n",
maskWord.toUpperCase(), mistakes, userLetterSet.toString().toUpperCase());
- Нужно разделить валидацию для ввода букв и для ввода команд
public static boolean inputValidation(char userInput) {
String regex = "[а-яА-ЯёЁ0-1]";
return Pattern.matches(regex, String.valueOf(userInput));
}
Вывод: в целом для процедурного стиля не так плохо.
#ревью #виселица