Викторина понял с MVC-паттерна (Ява)


Описание проекта

Программа здесь можно протестировать

Этот проект-упражнение, призванное проверить, если я правильно понял шаблон MVC и применить его правильно.

Тест должен быть рассчитан, в котором пользователь должен выбрать правильный ответ на вопрос с определенным набором ответов. Если он правильно отвечает на вопрос, пользователь получает одно очко. Если он ответит неверно, то программа выводит точки достигли, и прерывает выполнение программы.

Многие учебники объяснить паттерна MVC при условии, что есть только один класс, который принадлежит к модели, и в основном они называют это просто "модель". Так что это интересно для меня знать, если я применил правильно шаблон дизайна с несколькими классами модели.

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

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

Может контроллер отождествляться с чем-то вроде главного класса проекта? Можно ли включать логику программы?

Алгоритм:

  1. Показать вопрос и возможные ответы на него.
  2. Спросить у пользователя его предложение.
  3. Если он был прав: дать пользователю точки. Перейдите к шагу 1.
  4. Если он ошибся: выход из программы.

Исходные Файлы

Main.java

public class Main {
    public static void main(String[] args) {
        Question[] questionList = {
            new Question("In which country Kaiser Wilhelm II was born?",
            new String[] {
                "America",
                "Germany",
                "North Korea",
                "England"
            }, "B"),

            new Question("Which flowers are the most beautiful?",
            new String[] {
                "Tulips",
                "Roses",
                "Lilies",
                "Weeping willows"
            }, "C"),

            new Question("Where does England live?",
            new String[] {
                "On an island",
                "Near poland",
                "In the white house",
                "In he yellow house"
            }, "A"),

            new Question("Who's a free software activist?",
            new String[] {
                "Bill Gates",
                "Donald Trump",
                "Richard Stallman",
                "The GNU operating system"
            }, "C"),


            new Question("Which MMORPG has the most players?",
            new String[] {
                "Arthoria.de",
                "Nostale",
                "GTA 5",
                "World of Warcraft"
            }, "D")
        };

        // problem: the players name can not be initialized with functions from controller 
        // before the controller is initialized
        Player player = new Player("");
        Questions questions = new Questions(questionList);
        View view = new View();

        Controller controller = new Controller(player, questions, view);
        controller.mainLoop();
    }       
}

Player.java

// this class represents a player with a name and a score
public class Player {
    // represents his name
    private String name;
    // represents his score 
    private int score;

    // generates a player with a given name and a score of 0
    public Player(String name) {
        this.name = name;
        score = 0;
    }

    public String getName() {
        return name;
    }

    public int getScore() {
        return score;
    }

    public void setName(String name) {
        this.name = name;
    }

    // increases score of player
    public void scorePoint() {
        score++;
    }
}

Question.java

// this class represents a question with a set of answers
public class Question {
    // represents the question
    private String question;
    // represents four possible answers
    private String[] answers;
    // represents the correct answer with a letter from A to symbol D
    private String correctAnswerLetter;

    // generates a question and needs a string that contains the question, a string list
    // with exactly four answers and a String that has a letter from A to D that points to the 
    // correct answer, throws IllegalArgumentException
    public Question(String question, String[] answers, String correctAnswerLetter) {
        if(answers.length > 4) {
            throw new IllegalArgumentException("answers can only have four strings");
        }
        if(!correctAnswerLetter.equalsIgnoreCase("A") && !correctAnswerLetter.equalsIgnoreCase("B")
        && !correctAnswerLetter.equalsIgnoreCase("C") && !correctAnswerLetter.equalsIgnoreCase("D")) {
           throw new IllegalArgumentException("the letter representing the correct" +
           " string can only have a value from A to D");
        }

        this.question = question;
        this.answers = answers;
        this.correctAnswerLetter = correctAnswerLetter;
    }

    public String getQuestion() {
        return question;
    }

    public String[] getAnswers() {
        return answers;
    }

    // checks if the given letter equalsIgnoreCase to the correct letter
    public boolean check(String letter) {
        return letter.equalsIgnoreCase(correctAnswerLetter);
    }
}

Questions.java

import java.util.Random;

// this class manages a list of questions
public class Questions {
    // represents a collection of questions
    Question[] questions;

    // generates a set of questions and needs a list of questions
    public Questions(Question[] questions) {
        this.questions = questions;
    }

    // returns a random chosen question from the list
    public Question getRandomQuestion() {
        Random random = new Random();
        int selection = random.nextInt(questions.length);
        return questions[selection];
    }
}

View.java

public class View {
    /* methods for question */

    public void printQuestion(Question question) {
        String[] answers = question.getAnswers();

        System.out.println(question.getQuestion() + "\n");
        System.out.println("A: " + answers[0]);
        System.out.println("B: " + answers[1]);
        System.out.println("C: " + answers[2]);
        System.out.println("D: " + answers[3]);
    }

    /* methods for player */ 

    public void printScoreOfPlayer(Player player) {
        System.out.println(player.getName() + " has reached " + player.getScore() + " points.");
    }

    /* methods for general game logic */

    public void printNameRequest() {
        System.out.print("Your name: ");
    }

    public void printInputRequest() {
        System.out.print("Please chose a letter: ");
    }

    public void printSuccessMessage() {
        System.out.println("That was right!\n");
    }

    public void printGameOverMessage() {
        System.out.println("This was wrong. Game over.");
    }
}

Controller.java

import java.util.Scanner;

public class Controller {
    private Player player;
    private Questions questions;
    private Scanner input;

    private View view;

    public Controller(Player player, Questions questions, View view) {
        this.player = player;
        this.questions = questions;
        input = new Scanner(System.in);

        this.view = view;
    }

    public String getString() {
        return input.next();
    }

    public String getGuessOfPlayer() {
        String guess = input.next();
        if(!guess.equalsIgnoreCase("A") && !guess.equalsIgnoreCase("B") 
        && !guess.equalsIgnoreCase("C") && !guess.equalsIgnoreCase("D")) {
            throw new IllegalArgumentException("Enter A, B, C or D");
        }
        return guess;
    }

    public void mainLoop() {
        view.printNameRequest();
        player.setName(getString());

        while(true) {
            Question actualQuestion = questions.getRandomQuestion();

            view.printQuestion(actualQuestion);
            view.printInputRequest();
            String input = getGuessOfPlayer();
            if(actualQuestion.check(input)) {
                player.scorePoint();
                view.printSuccessMessage();
            } else {
                view.printGameOverMessage();
                view.printScoreOfPlayer(player);
                break;
            }
        }
    }
}


851
1
задан 3 февраля 2018 в 09:02 Источник Поделиться
Комментарии
2 ответа

В MVC-это обычно неправильно (в основном из-за фреймворков). Но цель контроллера интерпретировать сигналы, поступающие от пользователей, чтобы обновить модель , а вид отображения модели. Так как вид и контроллер имеет ссылку на модель.

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

void start() {
view.askForPlayer();
String player = readLine();
model.setPlayer();

while (!model.isOver()) {
model.changeQuestion();
char answer = readAnswer();
model.answerCurrentQuestion(answer);
}
}

Этот вид используется только для отображения вещи по требованию контролера, как вы сделали. Но и при изменении модели. Это означает, что модель должна раскрыть некоторые поля да. Но это нарушение принципа encapuslation ? Вам нужно, чтобы отобразить что-то, так что вы должны иметь доступ к ней.

void onQuestionChanged() {
Question question = model.getCurrentQuestion();
String[] answers = question.getAnswers();
out.printf("%1$s%n", question.getQuestion());
for (int i=0; i<answers.length; i++) {
out.printf("%1$C: %2$s%n", 'A'+i, answers[i]);
}
out.print("Please chose a letter: ");
out.flush();
}

В модели содержится государство и логику вашего приложения (текущий вопрос и результат). Так что вы можете иметь одну модель изготовлена из различных предметов (состав). Это не проблема, а это облегчает работу с одного класса, которые инкапсулируют доступ к базовым объектам и соответствует понятию совокупности в DomainDrivenDesign. Я бы модели, которые используют int для определения currentQuestion но когда мнение спрошу, модель вернуть Questionтак это делает некоторые заключения. Вы также можете прочитать немного о распоряжаться (без Р).

void answerCurrentQuestion(char answer) {
Question question = questions[currentQuestion];
if ( question.check(answer) ) {
score ++;
onChange(Property.SCORE);
} else {
isOver = true;
onChange(Property.OVER);
}
}

Question getCurrentQuestion() {
return questions[currentQuestion];
}

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

public static void main(String[] args) {
QuizzModel model = new QuizzModel(questionList);
QuizzView view = new QuizzView(model);
QuizzController ctrl = new QuizzController(model, view);

ctrl.start();
}

Кроме того, вы можете улучшить немного свой код путем предоставления OutputStream и InputStream для вашего просмотра и контроллер, легче будет проверить. Я тоже фанат Formatter когда у вас много печатать в консоли. И как сказал @Flamaker2018 вы можете улучшить логику проверки, но и быть меньше ограничений на количество ответов.

(Вот хорошее объяснение паттерн MVC для справки : http://aspiringcraftsman.com/2007/08/25/interactive-application-architecture/)

2
ответ дан 6 февраля 2018 в 10:02 Источник Поделиться

Мой комментарий код:

Метод имена многих printXXXX()
Я предполагаю, что мы могли бы реферат что showQuestion()или displayName(), promptAnswer()

"А","в","С" появляется во многих местах. Я хотел бы предложить, имея

interface UserInput{
String CHOICE_1 = "A";
String CHOICE_2 = "B";
String CHOICE_3 = "C";
String CHOICE_4 = "D";
}

Что отразится на разных местах, как:

    System.out.println("A: " + answers[0]);
System.out.println("B: " + answers[1]);
System.out.println("C: " + answers[2]);
System.out.println("D: " + answers[3]);

new String[] {
"On an island",
"Near poland",
"In the white house",
"In he yellow house"
}, "A"),

И логику проверки

   if(!correctAnswerLetter.equalsIgnoreCase("A") && !correctAnswerLetter.equalsIgnoreCase("B")
&& !correctAnswerLetter.equalsIgnoreCase("C") && !correctAnswerLetter.equalsIgnoreCase("D")) {
throw new IllegalArgumentException("the letter representing the correct" +
" string can only have a value from A to D");
}

Потому что если вы выберете меня, II, ш, IV, позднее придется менять во многих местах вместо одного.

class Player{
......
public void scorePoint() {
// scoring player is probably not a place for pojo
// I would move it to class like quiz or game, but not much logic so far I guess
score+=POINT_FOR_CORRECT_ANSWER;
}

private static final int POINT_FOR_CORRECT_ANSWER = 1;
}

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

score += question.getScore() 

Потом нам связать вопрос со счетом пользователь получает.

Я бы извлечь логику проверки от вопрос и сохранить ее просто как контейнер.

class Question{
public Question getRandomQuestion() {
// removed new Random from here, I don't think we need to create 100 objects for 100 questions
int selection = random.nextInt(questions.length);
return questions[selection];
}

private static final Random random = new Random();
}

Для представления классов, у нас есть много системы.из.метод println() звонки - может быть имеет смысл абстрактного, что с системой.из.код println(строковое сообщение) в качестве отдельного метода.

void display(String message);
SystemOutput.display(message -> System.out.println(message));

И звонки будут systemOutput.дисплей("ваше сообщение здесь").

 public void printQuestion(Question question) {
String[] answers = question.getAnswers();

System.out.println(question.getQuestion() + "\n");
System.out.println("A: " + answers[0]);
System.out.println("B: " + answers[1]);
System.out.println("C: " + answers[2]);
System.out.println("D: " + answers[3]);
}

/* methods for player */

public void printScoreOfPlayer(Player player) {
System.out.println(player.getName() + " has reached " + player.getScore() + " points.");
}

/* methods for general game logic */

public void printNameRequest() {
System.out.print("Your name: ");
}

public void printInputRequest() {
System.out.print("Please chose a letter: ");
}

public void printSuccessMessage() {
System.out.println("That was right!\n");
}

public void printGameOverMessage() {
System.out.println("This was wrong. Game over.");
}

2
ответ дан 6 февраля 2018 в 05:02 Источник Поделиться