Skip to content

Latest commit

 

History

History
349 lines (241 loc) · 6.63 KB

File metadata and controls

349 lines (241 loc) · 6.63 KB

Design Pattern - Singleton

Le singleton est un Design Pattern de création qui permet de garantir qu’il ne sera possible de créer qu’une seule instance d’une classe donnée.

Exemple d’utilisation :

  • un objet représentant un composant matériel de l'ordinateur qui doit être restreint à une unique instanciation pour éviter des conflits de gestions des ressources du composant.
  • Connexion à une base de donnée : faire en sorte que le programme se connecte toujours à la même base.
  • Une classe d'interface

Ce singleton est très utilisé !

Construction pas à pas

Pour la présentaiton de ce premier singleton, je vous propose de le construire pas à pas.

Posons le problème suivant : Mon programme contient un unique objet 'singleton' qui sera utilisé jusqu’à la fin du programme. Comment faire pour être sur qu’il soit créé et le rendre unique tout au long du programme ?

La première idée est la suivante : on créé un classe singleton et on appel un objet au début de programme que l’on garde tout au long du programme.

On créé donc une classe singleton :

le .h :

class Singleton {
    public :
    // constructeur public
    Singleton(){nomSingleton="singleton";}
    
    std::string nom()const;
    
    private : 
    // donnée membre (pour l'illustration)
    std::string nomSingleton;
    
};

dans le cpp

Singleton::Singleton()
{
    nomSingleton="singleton";
}

std::string Singleton::nom() const
{
    return nomSingleton;
}

Donc dans le main :

void fonctionUseSingleton(const Singleton& singleton)
{
    std::cout << "J'utilise mon singleton " << singleton.nom() << std::endl;
}

int main(int argc, const char * argv[]) {

    //instancation du singleton
    Singleton singleton;
    
    //des trucs avec mon singtleton
    fonctionUseSingleton(singleton);
}

Problèmes :

  • chaque fonction/classe/structure devra faire appelle au singleton.
  • On n’a une pseudo-unicité : rien empêche de créer d'autre singleton plus tard

Comment rendre implicite l’utilisation du président – sans appel à chaque fonction ou classe ?

On pourrait définir une instance via une variable globale :

class Singleton {
    public :
    
    // variable globale 
    static Singleton* mon_singleton;
    
    // constructeur public
    Singleton();
    
    std::string nom()const;
    
    //private:
    ~Singleton();
    
    private : 
    // donnée membre (pour l'illustration)
    std::string nomSingleton;

};

et dans le cpp

//creation de la variable globale
Singleton* Singleton::mpresident = new Singleton();

Singleton::Singleton()
{
    nomSingleton="singleton";
}

std::string Singleton::nom() const
{
    return nomSingleton;
}

///destructeur :
Singleton::~Singleton()
{
    if (mon_singleton) delete mon_singleton;
}

Et dans le main :

void fonctionUseSingleton()
{
    std::cout << "J'utilise mon singleton " << Singleton::mon_singleton->nom() << std::endl;
}

int main(int argc, const char * argv[]) {

    //des trucs avec mon singtleton
    fonctionUseSingleton();
}

Mais on voudrait que le singleton ne puisse pas être modifié au cours du programme, ce qui est possible ici.

Comment rendre l’objet global impossible à changer ?

On peut rendre privée la variable globale et créer une fonction getInstance() qui renvoi le président const. Mais attention, il faut tout de même prévoir la création de l'objet !

class Singleton {
    public :
    
    // variable globale 
    static const Singleton* getInstance();
    
    // constructeur public
    Singleton();
    
    std::string nom()const;
    
    //private:
    ~Singleton();
    
    private : 
    // donnée membre (pour l'illustration)
    std::string nomSingleton;

    static Singleton* mon_singleton;

};

et dans le cpp :

Singleton* Singleton::mon_singleton = nullptr;

//creation de la variable globale
Singleton* Singleton::getInstance()
{ 
    if (!mon_singleton)
        mon_singleton = new Singleton();
    return mon_singleton;
}

Singleton::Singleton()
{
    nomSingleton="singleton";
}

std::string Singleton::nom() const
{
    return nomSingleton;
}

///destructeur :
Singleton::~Singleton()
{
    if (mon_singleton) delete mon_singleton;
}

Et dans le main :

void fonctionUseSingleton()
{
    std::cout << "J'utilise mon singleton " << Singleton::getInstance()->nom() << std::endl;
}

int main(int argc, const char * argv[]) {

    //des trucs avec mon singtleton
    fonctionUseSingleton();
}

Mais là encore, rien interdit de créer un nouveau singleton...

Comment rendre impossible la création d’un autre président ?

Pour cela, on peut rendre privé le constructeur.

class Singleton {
    public :
    
    // variable globale 
    static const Singleton* getInstance();
        
    std::string nom()const;
    
    //private:
    
    // constructeur public
    Singleton();
    
    ~Singleton();
    
    private : 
    // donnée membre (pour l'illustration)
    std::string nomSingleton;

    static Singleton* mon_singleton;

};

et dans le cpp :

//creation de la variable globale
Singleton* Singleton::getInstance()
{ 
    if (!mon_singleton)
        mon_singleton = new Singleton();
    return mon_singleton;
}

Singleton::Singleton()
{
    nomSingleton="singleton";
}

std::string Singleton::nom() const
{
    return nomSingleton;
}

///destructeur :
Singleton::~Singleton()
{
    if (mon_singleton) delete mon_singleton;
}

Et dans le main :

void fonctionUseSingleton()
{
    std::cout << "J'utilise mon singleton " << Singleton::getInstance()->nom() << std::endl;
}

int main(int argc, const char * argv[]) {

    //des trucs avec mon singtleton
    fonctionUseSingleton();
}

Et voilà !

Le singleton

Dans sa version minimale, le singleton s'écrit ainsi :

Le .h :

class Singleton
{
    private:
        /* L'instance du singleton. */
        static Singleton* instance;

        /* Le constructeur privé. */
        Singleton();

    public:
        /* L'accès static à l'instance. */
        static Singleton* getInstance();
};

Le .cpp :

Singleton* Singleton::instance = NULL;

Singleton* Singleton::getInstance()
{
    if (instance == 0)
    {
        instance = new Singleton();
    }

    return instance;
}

Singleton::Singleton()
{}

Pourquoi on a retiré le destructeur ?

  • On ne fabrique qu‘un seul objet donc la fuite est faible
  • on n’est pas sur de déclarer l’objet (uniquement sur demande)
  • aucun intérêt de détruire l’objet avant la fin du programme

Exercice

Exercice 20