Guess Number Game — консольная игра с использованием Java

Эта игра довольно распространена для начинающих проектов. Я написал версию этого раньше всего в Main — поэтому здесь я ставлю перед собой задачу воссоздать это в более объектно-ориентированном стиле.

Я хотел использовать более объектно-ориентированный подход, поэтому я буду благодарен за любые отзывы о том, где я могу добиться большего успеха или делаю некоторые ошибки. У меня также есть некоторая личная путаница, если в некоторых частях я кодирую это процедурно. Я могу ошибаться, но я думаю, что этот код частично соответствует обоим стилям, и если да, может ли кто-нибудь помочь объяснить отличительные различия? В некоторых частях слишком много рекурсии? Как появляется читабельность? о чем ты думаешь?

Игра сгенерирует случайное секретное число и попросит пользователя ввести свои предположения в течение заданного количества попыток. Код ниже:

import java.util.InputMismatchException;
import java.util.Random;
import java.util.Scanner;

public class GuessGame {

    private static final Scanner SC = new Scanner(System.in);
    private static final Random R = new Random();
    private static int secret = R.nextInt(100) + 1;
    private static final int GIVEN_ATTEMPTS = 10;
    private int attempts = 0;

    public void playGame() {
        promptGuess();
    }

    private int promptGuess() {
        try {
        printLine("Guess a number from 1-100");
        int guess = SC.nextInt();
            if (guess < 1) {
                promptGuess();
            } else {
                return compare(guess);
            }
        } catch(InputMismatchException e) {
            printLine("Only numbers please");
            SC.nextLine();
            promptGuess();
        }
        return 0;
    }

    private int compare(int guess) {
        attempts++;
        if (attempts == GIVEN_ATTEMPTS) {
            return promptResult(4);
        }
        int difference = secret - guess;

        if (difference > 0) {
            return promptResult(1);
        } else if (difference < 0) {
            return promptResult(2);
        } else {
            return promptResult(3);
        }
    }

    private int promptResult(int n) {
        switch (n) {
            case 1:
                printLine("Guess was too low, try again");
                promptGuess();
                break;
            case 2:
                printLine("Guess was too high, try again");
                promptGuess();
                break;
            case 3:
                printLine("You got it! Nice guess! The number was " + secret);
                printLine("In " + attempts + " attempts");
                printLine("Game Over");
                attempts = 0;
                secret = R.nextInt(100) + 1;
                playAgain();
            case 4:
                printLine("Too many guesses! " + GIVEN_ATTEMPTS + "/" + GIVEN_ATTEMPTS + " used.");
                printLine("Secret number was: " + secret + " Game Over");
                attempts = 0;
                secret = R.nextInt(100) + 1;
                playAgain();

        }
        return 0;
    }

    private void printLine(String s) {
        System.out.println(s);
    }

    private void playAgain() {

        printLine("Play again? (Enter Y for yes or N for no)");
        String reply = SC.next();
        if (reply.equalsIgnoreCase("Y")) {
            playGame();
        } else if (reply.equalsIgnoreCase("N")) {
            printLine("Thanks for playing");
            System.exit(0);
        } else {
                System.out.println("Please enter a valid answer");
                playAgain();
            }
        }
    }


    public class Main {

    public static void main(String[] args) {
        GuessGame g = new GuessGame();

        g.playGame();
    }
}

1 ответ
1

Любопытно, что этот код очень похож на код в этом вопросе. Я не говорю, что у вас есть другая учетная запись, но вы случайно не из того же класса или что-то в этом роде?


Ваше форматирование кажется непоследовательным, используйте автоматическое форматирование кода (скорее всего, в вашей IDE он есть).


private static final Scanner SC = new Scanner(System.in);

Не сокращайте имена только потому, что можете, это затруднит понимание и поддержку кода.

Скорее всего, следует ограничить использование сканера (или потоков в целом). Потоки довольно легко ассоциируются с собственными ресурсами, которые для освобождения должны быть явно уничтожены.


    private static final Scanner SC = new Scanner(System.in);
    private static final Random R = new Random();
    private static int secret = R.nextInt(100) + 1;
    private static final int GIVEN_ATTEMPTS = 10;
    private int attempts = 0;

Некоторые из вашего состояния static, все ваши методы instance. Скорее всего, вы хотите, чтобы все ваше состояние instance.


           if (guess < 1) {
                promptGuess();
            } else {
                return compare(guess);
            }

Здесь вы рекурсивно повторяете, поэтому нажатие Return без ввода чего-либо должно в какой-то момент привести к сбою вашей игры из-за переполнения стека.


if (attempts == GIVEN_ATTEMPTS) {

Проверка на большее качество будет безопаснее.


private int promptResult(int n) {

Это не запрашивает результат, а показывает, было ли предположение верным или нет, метод следует переименовать.


        switch (n) {
            case 1:

Вместо того, чтобы использовать здесь числа, вы можете использовать константы или перечисление. Таким образом, никому не нужно будет помнить об этом 2 означает «слишком высоко».


    private void printLine(String s) {
        System.out.println(s);
    }

Хорошая идея, но вы получаете очень и очень мало, когда дело касается читабельности с помощью этого метода.


System.exit(0);

Следует иметь в виду, что System.exit не «выйти из приложения», а «убить процесс JVM». При вызове этого даже не finally блоки могут работать. В данном случае это не имеет значения, но нужно иметь в виду.


Ваша логика повсюду. Вместо того, чтобы связывать методы вместе, как вы, вам следует подумать о наличии основного метода, который обрабатывает поток и вызывает методы по мере необходимости. Таким образом, у вас будет логика вашей игры в одном месте, но «детали» будут скрыты в функциях, что позволит довольно легко не только понять, как работает игра, но и упростить изменение отдельных функции.

Что касается того, как сделать это более правильным, то здесь особо нечего делать, кроме очистки смешанного состояния. Начать можно с того, чтобы логика игры была отделена от логики вывода на терминал. Так что у вас будет Game класс, и тот примет GameUi интерфейс как параметр, например:

public interface GameUi {
    public abstract int getGuess();
    // ...
}

public class SimpleTerminalGameUi implements GameUi;

public class Game {
    public Game(GameUi gameUi);
    
    // Somewhere in your logic:
    int guess = gameUi.getGuess();
}

new Game(new SimpleTerminalGameUi());

Это позволит вам отделить презентацию от внутреннего состояния игры.

В качестве дальнейшего упражнения проделайте то же самое с секретным провайдером.

Я могу ошибаться, но я думаю, что этот код частично соответствует обоим стилям, и если да, может ли кто-нибудь помочь объяснить отличительные различия?

Ваш код вообще не объектно-ориентирован, вы просто используете один объект в качестве контейнера. Но, честно говоря, не так уж много логики для начала раскладывать на разные объекты.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *