Domanda

Sto costruendo un'app e ho bisogno della saggezza della comunità SO su un problema di progettazione.

Nella mia applicazione, ci deve essere ESATTAMENTE un'istanza della classe UiConnectionList , UiReader e UiNotifier .

Ora, ho immaginato due modi per farlo:

Metodo 1: Ogni file ha un'istanza globale di quella classe nel file di intestazione stesso.

Metodo 2: esiste un file globals.h separato che contiene singole istanze globali di ogni classe.

Codice di esempio:

Metodo 1

file: uiconnectionlist.h

#ifndef UICONNECTIONLIST_H
#define UICONNECTIONLIST_H

#include <QObject>
#include <QList>

class UiConnection;

class UiConnectionList : public QObject
{
    Q_OBJECT
public:
    UiConnectionList();

    void addConnection(UiConnection* conn);
    void removeConnection(UiConnection* conn);
private:
    QList<UiConnection*> connList;
};

namespace Globals {
    UiConnectionList connectionList;
}

#endif // UICONNECTIONLIST_H

file: uinotifier.h

#ifndef UINOTIFIER_H
#define UINOTIFIER_H

class UiNotifier
{
public:
    UiNotifier();
};

namespace Globals {
    UiNotifier uiNotifier;
}

#endif // UINOTIFIER_H

Metodo 2:

file: uiconnectionlist.h

#ifndef UICONNECTIONLIST_H
#define UICONNECTIONLIST_H

#include <QObject>
#include <QList>

class UiConnection;

class UiConnectionList : public QObject
{
    Q_OBJECT
public:
    UiConnectionList();

    void addConnection(UiConnection* conn);
    void removeConnection(UiConnection* conn);
private:
    QList<UiConnection*> connList;
};

#endif // UICONNECTIONLIST_H

file: uinotifier.h

#ifndef UINOTIFIER_H
#define UINOTIFIER_H

class UiNotifier
{
public:
    UiNotifier();
};

#endif // UINOTIFIER_H

file: globals.h

#ifndef GLOBALS_H
#define GLOBALS_H

#include "uiconnectionlist.h"
#include "uinotifier.h"

namespace Globals {
    UiConnectionList connectionList;
    UiNotifier uiNotifier;
}

#endif // GLOBALS_H

La mia domanda

Qual è il modo migliore / giusto per farlo?

PS: non penso che singleton sia la risposta giusta qui, vero?

Grazie


Ok, quindi due risposte mi hanno detto di creare istanze di UiConnectionList e UiNotifier , opzionalmente racchiuderlo in un UiContext e passarlo in giro ovunque richiesto.

Qualcuno potrebbe elencare i motivi (con esempi) per cui passare il contesto è meglio che avere variabili accessibili a livello globale.

Questo mi aiuterà a giudicare quale metodo è migliore (o più adatto alla mia app).

Grazie

È stato utile?

Soluzione

Con il tuo utilizzo in globals.h avrai una definizione multipla di Globals :: UiConnectionList e Globals :: UiNotifier per ogni unità di compilazione (file .cc o .cpp) che usi. Questo non è il modo per fare esattamente un'istanza di quei clasi. Dovresti usare lo schema singleton come suggerito dai precedenti poster.

Se non si desidera utilizzare il modello singleton, il modo corretto è definire entrambi i clasi in un'unità di compilazione e quindi dichiararli come extern nel file di intestazione, il risultato è l'istanza globale prevista della classe, ma ciò non impedisce che venga copiata o costruita. Dal tuo esempio non conosci la differenza tra dichiarazione e definizione.

Altri suggerimenti

Non li farei globali, invece creerei i tre oggetti nel tuo principale e li passerei dove sono necessari. È più facile seguire per qualche altro programmatore perché vede quando / dove vengono utilizzati. Ti dà anche un migliore controllo su quando crearli e distruggerli che se li dichiari globali.

EDIT: per chiarire normalmente i programmi diventano sempre più complessi col passare del tempo con il codice che viene aggiunto da vari sviluppatori con idee diverse sul design ecc. In generale (IMHO) una volta che inizi a introdurre globali in un programma, incoraggia altri programmatori a fare lo stesso. Questo è il motivo per cui preferisco che i dati vengano trasmessi ovunque siano utilizzati, in un linguaggio procedurale come argomento o in un linguaggio OOP trasmesso tramite il ctor. È quindi più facile vedere le dipendenze.

Prima di tutto, hai ragione. Questa domanda non ha nulla a che fare con il modello Singleton. È una questione di design di classe.

In questo caso preferirei un'implementazione diversa dalla tua. In entrambi i tuoi esempi usi uno spazio dei nomi chiamato " Global " ;. Questo sta rompendo il principio della singola preoccupazione, perché qui ci sono molti oggetti che non hanno nient'altro in comune che essere single accessibili a livello globale. Invece di farlo, dovresti incapsulare i tuoi singoli nella dichiarazione di classe stessa.

Guarda questo:

class UiConnectionList : public QObject
{
    Q_OBJECT

public:
    static UiConnectionList Connections; // This is your global varaible

public:
    UiConnectionList();

    void addConnection(UiConnection* conn);
    void removeConnection(UiConnection* conn);
private:
    QList<UiConnection*> connList;

};

Ora puoi accedere alle tue connessioni globali tramite UiConnectionList :: Connections. L'implementazione di singleton come variabile statica non è davvero buona e dovrebbe essere fatta meglio. Soprattutto per impedire il cambio del puntatore. Ma questa è una domanda diversa.

Il minimo che puoi fare è creare una classe / struttura UiContext. Definisci tutte le altre cose come variabili membro di questa classe. Quindi crea un'istanza di UiContext nella tua classe principale e passala alla classe che lo richiede.

Anche se, come già menzionato, la tua soluzione è difettosa, eviterei di usare un singleton. L'uso di singleton per raggiungere il tuo obiettivo renderà il tuo codice difficile da testare. Invece le classi dovrebbero dipendere da una pura interfaccia virtuale IConnetion o simili. L'invio di istanze agli oggetti quando vengono creati non sarebbe plausibile? Dovresti almeno avere la possibilità di farlo (o preferibilmente usando un setter) per rendere testabile il tuo codice. Tieni presente che " extern " la soluzione proposta da Piotr più o meno è la stessa di un singleton.

Molte persone non sono d'accordo sull'opportunità di utilizzare il modello globale / singleton. Personalmente non mi piace semplicemente perché va contro il concetto di design di classe vagamente accoppiato.

Ogni classe dovrebbe fare una cosa e dovrebbe poter esistere da sola il più possibile. Fare in modo che le classi utilizzino un'istanza UiConnectionList globale non solo crea problemi di manutenibilità, ma significa anche che le classi devono sapere da dove stanno ottenendo la loro istanza quando dovrebbero preferibilmente dire quale lista usare quando vengono create.

Pensa a una risorsa e al suo gestore.

Resource *ResouceManager::createResource (string name)
{
    Resource *res = new Resource(this);
    res->SetName(name);
    resourceList->Add(res);
    return res;
}

In questo esempio, la risorsa e il suo gestore sono accoppiati in modo molto modesto. Il manager può trovare le sue risorse create e la risorsa sa in quale manager è stata creata, ma alla risorsa viene detto a quale manager appartiene, non definendola da sola (attraverso un gestore globale).

L'altro modo è quello di creare una risorsa (o una sottoclasse), quindi chiedere a un Manager di aggiungerlo al suo elenco essenzialmente temporaneamente accoppiandoli, ma fino ad allora esistono separatamente e non si basano su istanze predefinite.

Il modo migliore per farlo è usare il metodo Singleton. È stato testato e provato. Inoltre, la classe fallirebbe se definissi un'altra variabile UiConnectionList, ad esempio nel mio ambito locale.

void myfunction()
{
    UiConnectionList connectionList;
    // Any usage to connectionList would be cleared after this function exits.
}

Ricorda sempre quando crei una classe singleton. Lock (Private-tify?) The big four: Costruttore, Costruttore copia, Operatore assegnazione, Distruttore

Inoltre, dal momento che stai usando variabili globali, presumo che tu non abbia bisogno di scopare o nasconderti. Quindi il metodo singleton è il modo migliore per farlo.

Ecco un esempio sull'implementazione di un singleton.

// Meyers singleton
static UiConnectionList* UiConnectionList::getSingletonPtr()
{
    static UiConnectionList x;
    return &x;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top