Design Singleton per progetti di lettori di musica di piccole / medie dimensioni

5

Sto refactoring il codice per un lettore musicale che ho scritto in Java / JavaFX.

Ho alcuni oggetti chiave a cui si accede in tutto il programma da varie altre classi. Penso che ce ne saranno quattro o cinque prima che finisca di spostare tutto intorno. C'è sempre un'istanza di questi oggetti istanziati per la vita del programma.

Questo approccio consente di rendere facilmente disponibili questi oggetti chiave quando è necessaria una buona architettura? C'è un approccio migliore a questo problema?

public class Hypnos extends Application {

    private static AudioPlayer player;
    private static FXUI gui;
    private static Library library;
    private static Queue queue;

    ... 

    public static AudioPlayer getPlayer() { 
        if ( audioPlayer == null ) {
            throw new IllegalStateException();
        }

        return player;
    }

    public static FXUI getGUI() {
        if ( ui == null ) {
            throw new IllegalStateException();
        }
        return gui;
    }

    //etc 

    @Override
    public void start ( Stage stage ) {
        ...
        library = new Library();
        queue = new Queue();
        player = new AudioPlayer();
        gui = new FXUI ( stage );
        ...
    }

    public static void main ( String[] args ) {
        launch( args ); //This calls start() above. 
    }
}

Ora, quando ne ho bisogno, posso semplicemente chiamare Hypnos.getPlayer() o Hypnos.getGUI() , senza dover passare un gruppo di oggetti attraverso le varie classi e metodi.

    
posta JoshuaD 02.07.2017 - 02:32
fonte

2 risposte

11

Is this approach to making these key Objects easily available when needed good architecture? Is there a better approach to this issue?

Il tanto singolato schema di singleton occasionalmente ha i suoi usi ma non lo vedo qui. Dici di avere quattro o cinque oggetti chiave necessari per "varie altre classi". Lascia che ti mostri un'alternativa più flessibile e rispetta il requisito "solo una istanza".

public static void main(String[] args) {
    Stage stage = launch(args);

    Library library = new Library();
    Queue queue = new Queue();
    AudioPlayer player = new AudioPlayer();
    FXUI gui = new FXUI(stage);

    Resources keyResources = new KeyResources(library, queue, player, gui);

    VariousOtherClass1 variousOtherClass1 = new VariousOtherClass1(keyResources);
    VariousOtherClass2 variousOtherClass2 = new VariousOtherClass2(keyResources);
    VariousOtherClass3 variousOtherClass3 = new VariousOtherClass1(
        keyResources, 
        variousOtherClass1, 
        variousOtherClass2);

    variousOtherClass3.start();
}

Crealo solo in main, una volta, e avrà sempre una sola istanza. Questo è un buon passaggio di riferimento vecchio stile. Il nuovo termine elegante è Dependency Injection. Il modello è semplice, costruisci ciò di cui hai bisogno, collegali tra loro e inizia uno di essi. Ciò mantiene il comportamento dal mescolare con la costruzione.

KeyResources (un nome che potrebbe sopportare miglioramenti) è un oggetto parametro . Fatto in questo modo, tutte queste risorse possono essere sostituite senza dover riscrivere KeyResources .

Il vantaggio principale qui è che le VarieVersioni (un nome che necessita disperatamente di miglioramenti) NON CONOSCONO dove trovare le loro risorse. Vengono a sapere quali sono le loro risorse. Ciò significa che non impongono di avere una risorsa globale. Potrebbero essere date loro diverse risorse senza richiedere una riscrittura. Che se alcuni improvvisamente hanno bisogno di risorse diverse gli altri non si rompono perché hai cambiato l'unico posto in cui vengono definiti. E rende esplicita la loro dipendenza da queste risorse. Oh, e certo, test.

    
risposta data 02.07.2017 - 05:31
fonte
1

Is this approach to making these key Objects easily available when needed good architecture? Is there a better approach to this issue?

La semplice disponibilità per gli oggetti necessari in un contesto non influisce sull'architettura del soggetto. Gli oggetti che sono necessari dovrebbero SEMPRE essere facilmente disponibili.

La domanda più interessante a cui la maggior parte degli sviluppatori non pensa: è davvero necessario rendere certi oggetti facilmente accessibili?

Puoi avere due lati. Ognuno di loro forse necessario o no. Devi decidere corretto. Quindi ci sono 4 possibili risultati:

  1. Accesso facile - > neccessary
  2. Accesso facile - > inutili
  3. Accesso difficile - > neccessary
  4. Accesso difficile - > inutile

Anche se 1. ha ovviamente un accesso facile e intuitivo (2.) aumenterà il numero di dipendenze da gestire all'interno di un sistema. Cioè con l'accesso statico selvaggio ai singleton può fare del male alla tua architettura.

Le decisioni più difficili sono quando sembra che sia un problema accedere a un oggetto speciale. Devi stare molto attento a rompere l'isolamento e introdurre nuove dipendenze è una decisione molto importante. Se interrompi l'isolamento tutto il tempo perché vuoi avere accesso ad un oggetto, questo causerà anche danni alla tua architettura.

Quindi "accesso difficile agli oggetti" è tuo amico. È un indicatore per le decisioni di architettura da fare.

Al tuo codice:

public static AudioPlayer getPlayer() { 
    if ( audioPlayer == null ) {
        throw new IllegalStateException();
    }

    return player;
}

Qui hai uno schema di stato nascosto mentre lanci un'eccezione se la var statica non è inizializzata dal metodo start. Inoltre, hai ampliato l'ambito della classe con l'ambito dell'oggetto in modo problematico.

È un design scarso dipendere dal tempo quando si tratta di inizializzare la variabile. Qui un puro Pattern Singleton è molto meglio avere SEMPRE una variabile / oggetto inizializzato. Inoltre ti astraggo dal processo di creazione dell'oggetto. Ma prendo in considerazione queste due possibilità solo perché ci sono molti più approcci migliori. Ma puoi andare con questo:

public class AudioPlayer {

    AudioPlayer () {
    }

    public static AudioPlayer getPlayer() { 
        if ( audioPlayer == null ) {
            player = new Audioplayer();
        }

        return player;
    }

}

[...] without having to pass and cache a bunch of objects around through the various classes and methods. [...]

Questa è la ragione principale per cui utilizzo i singleton per gli oggetti che hanno un carattere globale e qualsiasi altro ambito non ha senso. Un buon indicatore per un oggetto singleton è esattamente: se devi passare l'oggetto a qualsiasi classe e metodo nel tuo sistema. Se la sicurezza è importante per l'applicazione, l'oggetto più ovvio per essere accessibile a livello globale è l'utente che ha effettuato l'accesso. Quindi non ha senso passare questo oggetto attraverso l'intera applicazione in quanto ciò porterà a un sacco di codice della piastra della caldaia e di firme del metodo inespressivo.

Dopo tutto devi considerare quanto è grande la tua applicazione. Più grande è la tua applicazione, più le decisioni architettoniche diventano importanti. Se sviluppi questa applicazione da sola, affronterai problemi architettonici più tardi rispetto a quando ti stai sviluppando in un team.

Non voglio essere scortese, ma non penso che un lettore musicale sviluppato da una persona corrisponda a una dimensione in cui le decisioni architettoniche diventano rilevanti. Ti suggerisco alcune misure di base per la qualità del codice. Forse pubblichi un codice su "Stack Overflow - Code Review" per ottenere feedback sulla qualità del codice.

    
risposta data 02.07.2017 - 20:07
fonte

Leggi altre domande sui tag