Quale di questi disegni è preferito? [duplicare]

2

Nel caso di un'applicazione con una sola semplice responsabilità (ad esempio, una semplice sostituzione di grep o wc ), quale di questi disegni è preferito e perché? Trovo che siano tutti testabili e fanno tutti il loro lavoro. Qualcuno di loro ha vantaggi significativi rispetto all'altro?

1: Tutti i metodi nella classe principale:

public class App {
    public static void main(String[] args)
    {
        App app = new App();
        AppInput input = app.readInput();
        AppOutput output = app.processInputToOutput(input);
        app.writeOutput(output);
    }

    public AppInput readInput() { .. }
    public AppOutput processInputToOutput(AppInput input) { .. }
    public void writeOutput(AppOutput output) { .. }
}

2: metodi di base in una classe separata, la classe principale esegue solo chiamate per leggere ed elaborare l'input e il risultato di output:

public class App {
    public static void main(String[] args)
    {
        AppWorker worker = new AppWorker();
        AppInput input = worker.readInput();
        AppOutput output = worker.processInputToOutput(input);
        worker.writeOutput(output);
    }
}

public class AppWorker {
    public AppInput readInput() { .. }
    public AppOutput processInputToOutput(AppInput input) { .. }
    public void writeOutput(AppOutput output) { .. }
}

3: avere tutto il lavoro in una classe worker separata, con la classe principale che istanzia solo il worker e lo attiva per fare il suo lavoro:

public class App {
    public static void main(String[] args)
    {
        AppWorker worker = new AppWorker();
        worker.doYourWork();
    }
}

public class AppWorker {
    public void doYourWork()
    {
        AppInput input = readInput();
        AppOutput output = processInputToOutput(input);
        writeOutput(output);
    }

    public AppInput readInput() { .. }
    public AppOutput processInputToOutput(AppInput input) { .. }
    public void writeOutput(AppOutput output) { .. }

}
    
posta stackular 26.11.2013 - 10:41
fonte

3 risposte

5

Si noti che per la maggior parte si tratta di dettagli nitpick e si riferiscono alla migliore pratica generale del codice di strutturazione, tuttavia sarei arrogante supporre di sapere come si dovrebbe scrivere il programma.

Detto questo, diamo un'occhiata a # 1:

Soluzione n. 1

// Name of class containing main should usually be called Main and 
// Main in of itself is a single responsibility class, meaning it does nothing
// other than launch your program
public class App {
    public static void main(String[] args)
    {
        // Here, "App" should be a more significant name relevant to what it does.
        // Perhaps "Pipe" would be better suited?
        App app = new App();
        AppInput input = app.readInput();
        AppOutput output = app.processInputToOutput(input);
        app.writeOutput(output);
    }

    // These methods would obviously belong to Pipe and not to App/Main
    public AppInput readInput() { .. }
    public AppOutput processInputToOutput(AppInput input) { .. }
    public void writeOutput(AppOutput output) { .. }
}

Soluzione n. 2

// Same discussion as above with name of class containing Main
public class App {
    public static void main(String[] args)
    {
        // Correcting minor error using "app" in the place of "worker"
        AppWorker worker = new AppWorker();
        AppInput input = worker.readInput();
        AppOutput output = worker.processInputToOutput(input);
        worker.writeOutput(output);
    }
}

// AppWorker seems to uphold the suggestions mentioned above, but the name is a problem.
// "Worker" makes me think of threads.  If it isn't a thread, you would probably be better
// off calling it with a name appropriate for its job (again, I propose Pipe as
// it takes input and writes output).  
public class AppWorker {
    // "readInput" and "writeOutput" are redundant.  Better left as "read/write"
    public AppInput readInput() { .. }
    public AppOutput processInputToOutput(AppInput input) { .. }
    public void writeOutput(AppOutput output) { .. }
}

Soluzione n. 3

// Same discussion as above with name of class containing Main
public class App {
    public static void main(String[] args)
    {
        // This is simply moving the job of Main to another class.  
        // In more complicated programs, this may even be smart.  Controller classes 
        // are usually used when Main begins to be cluttered, leaving the logic of
        // argument handling in Main and putting the rest in the controller class.
        // However in this case, "AppWorker" does nothing more than the work of App/Main
        // which makes AppWorker simply redundant.
        AppWorker worker = new AppWorker();
        worker.doYourWork();
    }
}

public class AppWorker {
    public void doYourWork()
    {
        // You mean to use this here I assume?
        // App app = new App();
        AppInput input = this.readInput();
        AppOutput output = this.processInputToOutput(input);
        this.writeOutput(output);
    }

    // If AppWorker is controller as it seems to be, the following methods need to be
    // protected or private as this class controls and is not controlled.  Only 
    // public methods aside from "execute" method should be to set optional parameters.
    public AppInput readInput() { .. }
    public AppOutput processInputToOutput(AppInput input) { .. }
    public void writeOutput(AppOutput output) { .. }
}

La mia soluzione

Se posso essere così sfacciato, applicando i punti sopra, lo scriverò come segue:

public class Main{
    public static void main(String[] args)
    {
        Pipe pipe = new Pipe();
        PipeInput input = pipe.read();
        PipeOutput output = pipe.processInputToOutput(input);
        pipe.write(output);
    }
}

public class Pipe {
    public PipeInput read() { .. }
    public PipeOutput processInputToOutput(PipeInput input) { .. }
    public void write(PipeOutput output) { .. }
}

Si noti che la logica non è stata rimossa da Main, ma è ancora lasciata semplice. Se vedessi che il mio Main stava diventando ingombrante, prenderei in considerazione la creazione di una classe per gestire alcuni dei dettagli più banali, ma non al punto in cui creerei una classe che fa tutto ciò che fa Main. Neanche quando voglio creare una classe controller, porto tutti la logica. Il controller può fare alcune ipotesi sull'input e Main può fornire i test necessari.

Il lavoro di App è ora chiamato Pipe . Si noti che segue il principio della responsabilità unica. Se volessi aggiungere più logica, farei attenzione a non aggiungerlo automaticamente a Pipe, dal momento che Pipe fa già tutto ciò che dovrebbe e niente di più. E infine, si noti che i nomi dei metodi precedentemente chiamati readInput e writeOutput ora sono semplicemente read e write , poiché nel contesto di Pipe , è chiaro cosa si intende per i parametri passati.

Ancora una volta, questi sono dettagli nitpicky. Sto semplicemente seguendo le migliori pratiche che ho imparato nel corso degli anni. Spero che anche tu possa trarre vantaggio da questi suggerimenti!

    
risposta data 26.11.2013 - 11:34
fonte
2

Raccomando di fare il dispositivo all-in-one, quindi refactoring in classi separate man mano che l'applicazione cresce. Non è necessario aggiungere complessità al design finché non è necessario. Come lo dividi in seguito dipende da come la tua applicazione sta crescendo:

  • Diversi tipi di input. Forse stai leggendo da String o da un flusso di rete anziché da un file. In questo caso, si desidera creare classi e / o interfacce sull'input e istanziarle da main.
  • Diversi tipi di output. Forse stai inviando a un database o una pagina web invece di un terminale. In questo caso, vuoi creare classi e / o interfacce sull'output e istanziarle da main.
  • Diversi tipi di elaborazione. In questo caso, desideri classi e / o interfacce per la fase di elaborazione e crea un'istanza di quelle da main.
  • Diversi tipi di attività principali. Forse stai aggiungendo un'opzione GUI. In questo caso, la tua idea di AppWorker separata per eseguire l'intera operazione è un buon refactoring, perché puoi creare AppWorkerCli e AppWorkerGUI .

Alcune persone cercano di evitare qualsiasi rearchitecting indovinando il modo in cui l'applicazione crescerà, ma nella mia esperienza, gli sviluppatori sono scettici.

    
risposta data 26.11.2013 - 15:07
fonte
1

Mi piacciono tutti e tre i modi di farlo, e la lunghezza del codice mi direbbe come dovrebbe essere organizzata. Non mi occupo di due o tre pagine di codice nel mio oggetto Application. Una volta che arriva ad essere più di questo, mi piace vedere uno sforzo maggiore per separare le responsabilità delle classi.

L'applicazione deve contenere l'intero codice o essere solo un boot-strapping per consentire l'esecuzione dell'applicazione. Cioè, dovrebbe creare il controller e le viste e quindi non essere coinvolto nel corpo principale dell'applicazione. Arrestare o errori eccezionali sarebbe bello da contenere all'interno della classe Application.

Nel caso in cui l'applicazione sia un lavoratore a breve termine, viene ridotta a dimensioni di codice per me.

    
risposta data 26.11.2013 - 14:10
fonte

Leggi altre domande sui tag