Qual è il modo corretto per questi metodi di funzionare?

1

Come posso progettare correttamente cosa dovrebbe fare un metodo o una funzione?

Per chiarire la domanda, ho intenzione di utilizzare un esempio di un semplice gioco di hangman (non GUI) che ho creato. Il design è semplice, ha una classe HangmanGame che ha la parola che l'utente deve indovinare e altre cose come i metodi che controllano il gioco. Ha anche una classe HangmanDriver che controlla il flusso del gioco e le chiamate ai metodi e così via.

1 dei metodi è quello di indurre gli utenti a indovinarlo e processarlo e lo fa chiamando 2 altri metodi privati (helper) all'interno della classe. Quello che segue è il codice (dopo il quale è la mia effettiva richiesta).

/**
 * This method gets and processes a users guess.
 */
public void getAndProcessGuess()
{
    this.processGuess(this.getGuess());
}

/**
 * Asks the user for their guess and validates that their
 * guess is legal and has not already been made.
 * 
 * @return char The letter the user guessed
 */
private char getGuess()
{
    java.util.Scanner input = new java.util.Scanner(System.in);
    String guess = "";
    char letter = '
/**
 * This method gets and processes a users guess.
 */
public void getAndProcessGuess()
{
    this.processGuess(this.getGuess());
}

/**
 * Asks the user for their guess and validates that their
 * guess is legal and has not already been made.
 * 
 * @return char The letter the user guessed
 */
private char getGuess()
{
    java.util.Scanner input = new java.util.Scanner(System.in);
    String guess = "";
    char letter = '%pre%';

    //get and validate the user input
    while (true)
    {
        System.out.print("Please enter your guess: ");
        try
        {
            guess = input.nextLine().toUpperCase();
        }
        catch (InputMismatchException e)
        {}

        System.out.println();

        if (guess.length() >= 1)
            letter = guess.charAt(0);

        if (!(guess.matches("[A-Z]{1}")))
            System.out.println("Please enter a single letter between A and Z\n");
        else if(isLetterGuessed((letter)))
            System.out.println("\nThat letter has already been guessed\n");
        else
            return letter;

        printSeparator(); //if the guess isn't valid print out a line separator
    }   
}   

/**
 * Processes the guess from user
 * 
 * @param letter the letter the user guessed
 */
private void processGuess(char letter)
{

    if (secretWord.indexOf(letter) >= 0)
    {
        int count = secretWord.length() - secretWord.replaceAll(Character.toString(letter), "").length();
        System.out.println("The letter " + letter + " appears " + count + (count == 1 ? " time." : " times.") + "\n");
        setLetterAsGuessed(letter);
        printSecretWordWithGuessedLettersShown();
    }
    else
    {
        System.out.println("The letter " + letter + " does not appear in the hidden word\n");
        decrementNumOfGuessesLeft();
        System.out.println("You have " + numOfGuessesLeft + " incorrect guesses left\n");
    }

    printSeparator();

}
'; //get and validate the user input while (true) { System.out.print("Please enter your guess: "); try { guess = input.nextLine().toUpperCase(); } catch (InputMismatchException e) {} System.out.println(); if (guess.length() >= 1) letter = guess.charAt(0); if (!(guess.matches("[A-Z]{1}"))) System.out.println("Please enter a single letter between A and Z\n"); else if(isLetterGuessed((letter))) System.out.println("\nThat letter has already been guessed\n"); else return letter; printSeparator(); //if the guess isn't valid print out a line separator } } /** * Processes the guess from user * * @param letter the letter the user guessed */ private void processGuess(char letter) { if (secretWord.indexOf(letter) >= 0) { int count = secretWord.length() - secretWord.replaceAll(Character.toString(letter), "").length(); System.out.println("The letter " + letter + " appears " + count + (count == 1 ? " time." : " times.") + "\n"); setLetterAsGuessed(letter); printSecretWordWithGuessedLettersShown(); } else { System.out.println("The letter " + letter + " does not appear in the hidden word\n"); decrementNumOfGuessesLeft(); System.out.println("You have " + numOfGuessesLeft + " incorrect guesses left\n"); } printSeparator(); }

Cosa si dovrebbe fare all'interno di ciascuno di questi metodi e se stanno facendo troppo?

Il problema che ho con il modo in cui è ora è che i metodi stampano le cose. Funziona nel modo in cui il programma funziona, ma sembra che questi metodi stiano facendo più di quanto dovrebbero e che forse la stampa dovrebbe essere fatta dalla classe Driver non dall'istanza della classe. Inoltre, se questo dovesse mai essere modificato in una GUI, il metodo non avrebbe molto senso. Il problema di non avere il metodo di stampare una riga è che la convalida dovrebbe essere eseguita anche nella classe del driver.

Il modo in cui lo vedo è che ci sono tre percorsi possibili:

Inheritance

Il gioco dell'impiccato può essere trasformato in una superclasse e quindi avrà 2 figli uno HangmanConsoleGame e un altro HangmanGUIGame. Questo risolverebbe il problema con il modo in cui i metodi funzionano in ogni classe (ma la domanda rimane ancora se i metodi dovrebbero stampare cose).

Mantieni come è

Funziona per come funziona ora il gioco. Se dovessero essere apportate delle modifiche o se il gioco verrà modificato in una versione della GUI, il codice potrà essere nuovamente rielaborato in un secondo momento in modo che funzioni per le modifiche. Ciò presuppone che i metodi di validazione e stampa siano ok.

Refactor Now

Modifica il codice ora in modo che la convalida venga eseguita all'interno della classe Driver e i metodi restituiscano semplicemente i valori con indicazioni se il valore non è valido. Con la classe Driver che esegue tutte le operazioni di stampa e i metodi per indurre gli utenti a indovinare solo i valori di ritorno, può essere facilmente modificata in una GUI in un secondo momento e riduce al minimo le operazioni eseguite dai metodi.

Quale di questi ha più senso se esiste? C'è un modo migliore per farlo?

    
posta yitzih 25.02.2015 - 17:01
fonte

4 risposte

3

Il problema principale che ho è che stai stampando per console da tutti i tuoi metodi. Se possibile, verifica se è possibile effettuare il refactoring del codice per restituire risultati significativi e quindi stampare (o leggere) su / da console da 1 posizione, ad esempio il driver.

Questo sarebbe utile se si desidera creare un programma GUI e ora la logica dovrà essere rewired per gestire finestre e pannelli effettivi, e questo potrebbe non essere un giro divertente. Se scudi la tua logica da ciò che significa che stai usando per comunicare con l'utente, ricablarlo dovrebbe essere più semplice.

In secondo luogo (la mia principale preoccupazione) è che stai creando un nuovo Scanner : java.util.Scanner input = new java.util.Scanner(System.in); , ma non lo hai mai effettivamente pubblicato, quindi la tua applicazione ha una perdita.

Infine, questo: (!(guess.matches("[A-Z]{1}"))) controllerà se la stringa ha almeno un carattere (maiuscolo). Tuttavia, stai prendendo il primo carattere di input, quindi, qualcosa del genere: 123t dovrebbe passare il controllo ma interrompere la tua logica. Usando qualcosa come: Char.isLetter(guess.charAt(0)) dovrebbe funzionare meglio, oppure cambiare il tuo pattern in (!(guess.matches("^[A-Za-z]"))) .

    
risposta data 25.02.2015 - 17:43
fonte
1

It works in the current way the program works but it feels like these methods are doing more than they should

Sono d'accordo, i tuoi metodi fanno troppo adesso.

Vorrei rifattore ora. Se si estrae il codice in classi, si renderà il codice più estensibile e sarà anche più facile trovare funzionalità. Vorrei creare una struttura come questa:

interface Input {
    char getGuess(List<Character> guessedLetters) throws IllegalInputException;
}

interface Output {
    void reportIncorrectInput(String message);
    void reportWrongGuess(String message, char guess);
    void reportRightGuess(String message, char guess);
}

class IllegalInputException extends Exception {
    public IllegalInputException(String s) {
        super(s);
    }
}

class ConsoleInput implements Input {
    @Override
    public char getGuess(List<Character> guessedLetters) throws IllegalInputException {
        // get input. if input is illegal, throw:
        throw new IllegalInputException("message depending on error");
    }
}

class ConsoleOutput implements Output {
    // report (in this case, all methods just print to console)
}

E poi usa le interfacce nel tuo controller di gioco. In questo modo, puoi facilmente scambiare l'implementazione concreta con un'altra (come una gui).

    
risposta data 25.02.2015 - 17:32
fonte
1

Penso che dipenda da quanto modulare vuoi che sia il tuo codice. Ho imparato a rendere la tua classe 'Driver / main' il più minimalista possibile, a mettere il duro lavoro il più lontano possibile per rendere il tuo codice il più possibile riutilizzabile. Ma è davvero una questione di opinione. Se ritieni che riscrivere blocchi del tuo codice sarà utile, fallo, ma non sprecare il tuo tempo in qualcosa che ti darà solo un magro ritorno.

Probabilmente c'è sempre un modo migliore, e assolutamente sempre un modo diverso di fare qualcosa nel codice, è solo una questione se pensi che ciò che hai fatto abbia il suo scopo.

Se c'è una buona possibilità di passare a un'applicazione basata sulla GUI, ti consiglio di fare l'idea della classe super / sub che hai menzionato. Ciò lo renderebbe più facile con abbondanza di sovrapposizioni tra i codici, quindi non devi scrivere tanto.

Ci sono alcune opzioni con cui puoi andare, dipende solo dalle tue preferenze personali.

    
risposta data 25.02.2015 - 16:53
fonte
1

In parole povere

  • Devi separare la logica di business dalla logica di presentazione.
  • La classe di gioco (business logic) non dovrebbe leggere l'input dalla console non da un modulo HTML.
  • La classe di gioco (business logic) dovrebbe comunicare con il mondo solo tramite le chiamate ai metodi.
  • Un'app console (logica di presentazione) dovrebbe chiedere input e chiamare metodi alla classe di gioco e inviare informazioni alla console
  • Un'app Web (anche la logica di presentazione) dovrebbe richiedere input e chiamare metodi alla classe di gioco e inviare a HMTL
  • Un'app desktop grafica (ancora un'altra logica di presentazione) dovrebbe chiedere input (magari una finestra di dialogo o eventi del mouse) e fare chiamate di metodo alla classe di gioco, e produrre graficamente in finestre, finestre di dialogo, ecc.
  • La classe di gioco è agnostica. Non adora la console Dio, il web Dio o il Dio del desktop.
risposta data 25.02.2015 - 19:02
fonte

Leggi altre domande sui tag