IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Delphi Discussion :

Variables globales section implémentation et objet singleton


Sujet :

Langage Delphi

  1. #1
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 858
    Points : 11 301
    Points
    11 301
    Billets dans le blog
    6
    Par défaut Variables globales section implémentation et objet singleton
    Bonjour,

    Je m'interrogeais sur la meilleure manière de faire pour un objet singleton utilisant des procédures requérant des variables globales.

    La structure actuelle est la suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    implementation
     
    var
      UneSeuleInstance: TMyObject = nil;
      // ici les variables utiles à mes procédures, settées par mon objet
      procVar1, procVar2: integer;
     
    procedure CallBack(aParam: integer);
    begin
     //...
    end;
     
    procedure TMyObject.Create;
    begin
      if Assigned(UneSeuleInstance)
      then raise Exception.Create('Une seule instance de TMyObject autorisée ; désolé.')
      else inherited Create;
      UneSeuleInstance:=self; // car singleton
      //...
    end;
    Serait-il plus "propre" de déclarer les procVar en :
    1) class var en "compliquant" leur appel dans les CallBacks (TMyObject.procVar1) ?
    2) membres privés de la classe, puisqu'une seule instance (UneSeuleInstance.procVar1) ?
    3) autre ?

    Merci pour vos avis !
    Delphi 5 Pro - Delphi 11.3 Alexandria Community Edition - CodeTyphon 6.90 sous Windows 10 ; CT 6.40 sous Ubuntu 18.04 (VM)
    . Ignorer la FAQ Delphi et les Cours et Tutoriels Delphi nuit gravement à notre code !

  2. #2
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 445
    Points
    28 445
    Par défaut
    bonjour,

    le singleton est instancié (a priori), il n'y a donc pas lieu d'avoir des membres de classe

    maintenant deux choses, soit tu fais de l'objet, soit tu n'en fais pas

    version "moins" objet
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    unit MonUnit;
     
    interface
     
    type
      TMyObject = class
      end;
     
    function GetInstance: TMyObject;
     
    implementation
     
    type
    // on peut en profiter pour ajouter des choses invisibles
      TMyObject2 = class(TMyObject)
      end;
     
    var
      Instance: TMyObject2 = nil;
     
    function GetIsntance: TMyObject;
    begin
      if Instance = nil then
       Instance := TMyObject2.Create;
      Result := Instance;
    end;
     
    end.
    version "plus" objet
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    unit MonUnit;
     
    interface
     
    type
      TMyObject = class
        class var Instance: TMyObject;
        class function GetInstance: TMyObject;
      end;
     
    implementation
     
    function TMyObject.GetInstance: TMyObject;
    begin
      if Instance = nil then
       Instance := TMyObject.Create;
      Result := Instance;
    end;
     
    end.
    tu peux aussi surcharger le constructeur pour lever une exception

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     
    unit MonUnit;
     
    interface
     
    type
      TMyObject = class
       private
        class var Instance: TMyObject;
        constructor CreateInstance;
       public
        constructor Create; override;
        class function GetInstance: TMyObject;
      end;
     
    implementation
     
    constructor TMyObject.Create;
    begin
       raise Exception.Create('Use TMyObject.GetInstance()');
    end;
     
    constructor TMyObject.CreateInstance;
    begin
      inherited Create;
    end;
     
    function TMyObject.GetInstance: TMyObject;
    begin
      if Instance = nil then
       Instance := TMyObject.CreateInstance;
      Result := Instance;
    end;
     
    end.
    NB: outre les questions philosophiques, il n'y a pas de différence technique entre la version "moins" objet et la version "plus" objet

    et dans les deux cas je considère qu'il faut retourner l'instance existante au lieu de lever une exception comme tu le fais.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  3. #3
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 858
    Points : 11 301
    Points
    11 301
    Billets dans le blog
    6
    Par défaut
    Merci, Paul, pour tes exemples et avoir alimenté ma réflexion
    Delphi 5 Pro - Delphi 11.3 Alexandria Community Edition - CodeTyphon 6.90 sous Windows 10 ; CT 6.40 sous Ubuntu 18.04 (VM)
    . Ignorer la FAQ Delphi et les Cours et Tutoriels Delphi nuit gravement à notre code !

  4. #4
    Membre émérite

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2007
    Messages
    3 388
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Novembre 2007
    Messages : 3 388
    Points : 2 999
    Points
    2 999
    Par défaut
    Paul, je trouve sympa ta troisième méthode sauf que ... pour le create, pas d'override

    Ensuite, j'ai essayé (juste en changeant le nom, donc sans conséquence):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
    uses SysUtils;
     
    type
     
      TSingleton = class
      private
        class var Instance: TSingleton;
        constructor CreateInstance;
      public
        constructor Create;
        class function GetInstance: TSingleton;
      end;
     
    implementation
     
    { TSingleton }
     
    constructor TSingleton.Create;
    begin
      raise Exception.Create('Use TSingleton.GetInstance()');
    end;
     
    constructor TSingleton.CreateInstance;
    begin
      inherited Create;
    end;
     
    class function TSingleton.GetInstance: TSingleton;
    begin
      if Instance = nil then
        Instance := TSingleton.CreateInstance;
      Result := Instance;
    end;
    et dans une fiche appelante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    uses UnitSingleton;
     
    var
      Singleton: UnitSingleton.TSingleton;
     
    procedure TForm2.Button1Click(Sender: TObject);
    begin
      Singleton := UnitSingleton.TSingleton.GetInstance;
      Singleton.Free;
    end;
    Le premier clic passe sans problème.
    Pour le second clic:
    ---------------------------
    Opération de pointeur incorrecte.
    ---------------------------

    ça devrait pourtant fonctionner non ??


    le troisième clic me renvoie une violation d'accès.

    Je suis sur un XE3

  5. #5
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 694
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 694
    Points : 13 130
    Points
    13 130
    Par défaut
    Citation Envoyé par Papy214 Voir le message
    ça devrait pourtant fonctionner non ??
    Que vaut Instance après Singleton.Free

  6. #6
    Membre émérite

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2007
    Messages
    3 388
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Novembre 2007
    Messages : 3 388
    Points : 2 999
    Points
    2 999
    Par défaut
    Je vais peut-être dire une connerie mais ... si ma variable Singleton représente la variable Instance, Instance est nil ...

    Ou alors, y'a un truc qui m'échappe

    J'ai remplacé le .Free par un FreeAndnil mais j'obtiens le même comportement

    Un peu bouché le papy aujourd'hui ????

  7. #7
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Pour XE2 et plus, avec les class var, class constructor et générique, on peut créer un modèle réutilisable Design Pattern : Singleton - Découverte des Génériques et Class Constructor

    @tourlourou, tu marques Delphi 5 dans ta signature, tu ne peux pas faire de class var, quel Delphi utilises-tu ?

    @Papy, tu confonds la référence stockée par la variable Instance interne à UnitSingleton et la référence stockée par la variable Singleton de l'unité appelante
    Lors du GetInstance
    La variable Singleton reçoit la référence contenue dans la variable Instance
    Tu as deux variables qui pointe sur la même référence

    un Free libère l'instance de UnitSingleton et laisse trainer une reference pointant sur un objet libéré dans la variable Instance (et aussi dans la variable Singleton)
    un FreeAndNil libère l'instance de UnitSingleton et passe à nil la variable Singleton de l'unité appelante mais laisse une reference pointant sur un objet libéré dans la variable Instance

    Ainsi lors du second GetInstance
    La variable Singleton reçoit cette référence pointant sur un objet libéré
    Ainsi le second Free tente de libérer un objet déjà libéré => Exception !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  8. #8
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 858
    Points : 11 301
    Points
    11 301
    Billets dans le blog
    6
    Par défaut
    Citation Envoyé par ShaiLeTroll
    tu marques Delphi 5 dans ta signature, tu ne peux pas faire de class var, quel Delphi utilises-tu ?
    Hi Hi, Lazarus ! Enfin, CodeTyphon !
    Delphi 5 Pro - Delphi 11.3 Alexandria Community Edition - CodeTyphon 6.90 sous Windows 10 ; CT 6.40 sous Ubuntu 18.04 (VM)
    . Ignorer la FAQ Delphi et les Cours et Tutoriels Delphi nuit gravement à notre code !

  9. #9
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 445
    Points
    28 445
    Par défaut
    on peut récupérer la chose en surchargeant Destroy afin de remettre Instance à nil
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  10. #10
    Membre émérite

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2007
    Messages
    3 388
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Novembre 2007
    Messages : 3 388
    Points : 2 999
    Points
    2 999
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    on peut récupérer la chose en surchargeant Destroy afin de remettre Instance à nil
    Effectivement, merci ! J'étais effectivement un peu bouché hier

    Quand à votre discussion entre Shai et toi sur le lien qu'a donné ce dernier, très intéressant ...
    Je vais mettre ça dans mes archives

  11. #11
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Citation Envoyé par tourlourou Voir le message
    Hi Hi, Lazarus ! Enfin, CodeTyphon !
    Ah Code Typhon est une variante de Lazarus, je ne connaissais pas du tout !


    Même si libérer le Singleton n'est pas une chose à faire
    perso, je n'ai pas pensé à protéger mon code de pattern puisque cela ne m'était pas venu à l'idée
    J'aurais aimer rajouter cette sécurité, si mes collègues ont un jour cette idée car mieux vaut être prudent
    mais avec la générique, la classe ignore qu'est singleton
    Car des certains projets, j'utilise la classe librement (souvent durant une courte période ou alors une par thread)
    et dans d'autres projets, la classe est uniquement utilisé via Singleton
    Je le ferais pour les classes qui sont strictement Singleton et qui le savent (celle qui utilise CheckInstance en interne)


    Si l'on repart du code de UnitSingleton cité par Papy
    Pour la remarque de Paul Toth effectivement, si Self = Instance alors on peut repasser Instance à nil
    Important
    ne pas le faire si l'on a une exception dans le constructeur qui appellerait le destructeur d'où le test "Self = Instance"

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    uses SysUtils;
     
    type
     
      TSingleton = class
      private
        class var Instance: TSingleton;
        constructor CreateInstance;
      public
        constructor Create;
        destructor Destroy; override;
        class function GetInstance: TSingleton;
      end;
     
    implementation
     
    { TSingleton }
     
    constructor TSingleton.Create;
    begin
      raise Exception.Create('Use TSingleton.GetInstance()');
    end;
     
    destructor TSingleton.Destroy;
    begin
      inherited Destroy();
     
      if Instance = Self then
        Instance := nil;
    end;
     
    constructor TSingleton.CreateInstance;
    begin
      inherited Create;
    end;
     
    class function TSingleton.GetInstance: TSingleton;
    begin
      if Instance = nil then
        Instance := TSingleton.CreateInstance;
      Result := Instance;
    end;
    EDIT La pattern avec sécurité sur le Free

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
     
    //------------------------------------------------------------------------------
    (*                SoLuTions is an Versatile Library for Delphi                 -
     *                                                                             -
     *  Version alternative publiée sur "www.developpez.net"                       -
     *  Post : "Design Pattern : Singleton - Découverte des Génériques et Class Constructor"
     *  Post Number : 7517437                                                      -
     *  Post URL = "http://www.developpez.net/forums/d1384395/environnements-developpement/delphi/contribuez/design-pattern-singleton-decouverte-generiques-class-constructor/#post7517437"
     *                                                                             -
     *  Copyright ou © ou Copr. "SLT Solutions", (2006)                            -
     *  contributeur : ShaiLeTroll (2013) - Remplacement d'un template utilisé via l'outil SLTCodeGenerator par les génériques Delphi
     *  contributeur : ShaiLeTroll (2013) - Documentation Insight                  -
     *                                                                             -
     * ShaiLeTroll@gmail.com                                                       -
     *                                                                             -
     * Ce logiciel est un programme informatique servant à aider les développeurs  -
     * Delphi avec une bibliothèque polyvalente, adaptable et fragmentable.        -
     *                                                                             -
     * Ce logiciel est régi par la licence CeCILL-C soumise au droit français et   -
     * respectant les principes de diffusion des logiciels libres. Vous pouvez     -
     * utiliser, modifier et/ou redistribuer ce programme sous les conditions      -
     * de la licence CeCILL-C telle que diffusée par le CEA, le CNRS et l'INRIA    -
     * sur le site "http://www.cecill.info".                                       -
     *                                                                             -
     * En contrepartie de l'accessibilité au code source et des droits de copie,   -
     * de modification et de redistribution accordés par cette licence, il n'est   -
     * offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,   -
     * seule une responsabilité restreinte pèse sur l'auteur du programme,  le     -
     * titulaire des droits patrimoniaux et les concédants successifs.             -
     *                                                                             -
     * A cet égard  l'attention de l'utilisateur est attirée sur les risques       -
     * associés au chargement,  à l'utilisation,  à la modification et/ou au       -
     * développement et à la reproduction du logiciel par l'utilisateur étant      -
     * donné sa spécificité de logiciel libre, qui peut le rendre complexe à       -
     * manipuler et qui le réserve donc à des développeurs et des professionnels   -
     * avertis possédant  des  connaissances  informatiques approfondies.  Les     -
     * utilisateurs sont donc invités à charger  et  tester  l'adéquation  du      -
     * logiciel à leurs besoins dans des conditions permettant d'assurer la        -
     * sécurité de leurs systèmes et ou de leurs données et, plus généralement,    -
     * à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.          -
     *                                                                             -
     * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez      -
     * pris connaissance de la licence CeCILL-C, et que vous en avez accepté les   -
     * termes.                                                                     -
     *                                                                             -
     *----------------------------------------------------------------------------*)
    unit SLT.Common.DesignPattern;
     
    interface
     
    uses System.SysUtils, System.SyncObjs;
     
    type
      { Forward class declarations }
      TSLTSingleton<T: class, constructor> = class;
      TSLTSingletonThreadSafe<T: class, constructor> = class;
     
      /// <summary>Erreur de base liée au Patron de Conception (Design Pattern)</summary>
      ESLTDesignPatternError = class(Exception);
     
      /// <summary>Erreur de base liée au Patron de Conception "Singleton"</summary>
      ESLTSingletonError = class(ESLTDesignPatternError)
      public
        type
          TErrorType = (setCheckInstanceAssertion);
      public
        /// <summary>Crée l'exception avec un message prédéfini</summary>
        constructor Create(ErrorType: TErrorType; ASingletonClass: TClass);
      end;
     
      /// <summary>Template aidant la création d'une classe respectant le Patron de Conception "Singleton"</summary>
      /// <remarks>Contrairement à un "Static Singleton" que l'on utilise uniquement via des méthodes et propriétés de classe,
      /// une "Singleton" fourni une instance unique offrant des méthodes et propriétés d'instance.
      /// Limiter votre utilisation d'un "Static Singleton" à des classes ne nécessitant que peu d'initialisation,
      /// pour une structure plus complexe le "Singleton" est préférable !</remarks>
      TSLTSingleton<T: class, constructor> = class(TObject)
      private
        class var
          FInstance: T;
      protected
        class function GetInstance(): T; static;
        class procedure CheckInstance(Obj: T);
        class procedure NotifyDestroy(Obj: T);
      public
        // Constructeurs de Classe
        class destructor Destroy();
        /// <summary>Masquage du constructeur non virtuel Create par cette méthode de classe</summary>
        class function Create(): T;
     
        // Propriétés de Classe
        class property Instance: T read GetInstance;
      end;
     
      /// <summary>Template aidant la création d'une classe respecant le Patron de Conception "Singleton" avec prise en compte des Threads</summary>
      TSLTSingletonThreadSafe<T: class, constructor> = class(TSLTSingleton<T>)
      private
        class var
          FInstance: T;
          FInstanceLock: System.SyncObjs.TCriticalSection;
      protected
        class function GetThreadSafeInstance(): T; static;
      public
        // Constructeurs de Classe
        class constructor Create();
        class destructor Destroy();
     
        // Propriétés de Classe
        class property Instance: T read GetThreadSafeInstance;
      end;
     
    //---------------------------------------------------------------------------
    (*            Conseil et Rappel de Syntaxe pour TSLTSingleton               -
                                                                                -
      le TSLTSingleton garanti que l'instance gérée sera unique et propose des protections contre des accès alternatifs
      On peut définir Get avec :                                                -
                                                                                -
        Result := TSLTSingleton<TMySingleton>.Instance;                         -
        Result := TSLTSingletonThreadSafe<TMySingleton>.Instance;               -
                                                                                -
      Il n'y aura qu'une seule et unique instance de la classe TMySingleton     -
                                                                                -
      L'utilisation de CheckInstance dans le constructor n'est pas obligatoire, -
      mais cela protège contre l'utilisation explicite du constructeur Create   -
      L'utilisation de NotifyDestroy dans le desstructor n'est pas obligatoire, -
      mais cela protège contre l'utilisation explicite de la méthode Free       -
                                                                                -
    //---------------------------------------------------------------------------
    type                                                                        -
      TMySingleton = class                                                      -
      public                                                                    -
        constructor Create;                                                     -
        class function Get: TMySingleton; static;                               -
      end;                                                                      -
                                                                                -
    constructor TMySingleton.Create();                                          -
    begin                                                                       -
      TSLTSingleton<TMySingleton>.CheckInstance(Self);                          -
                                                                                -
      inherited Create();                                                       -
    end;                                                                        -
                                                                                -
    destructor TMySingleton.Destroy();                                          -
    begin                                                                       -
      TSLTSingleton<TMySingleton>.NotifyDestroy(Self);                          -
                                                                                -
      inherited Destroy();                                                      -
    end;                                                                        -
                                                                                -
    class function TMySingleton.Get: TMySingleton;                              -
    begin                                                                       -
      Result := TSLTSingleton<TMySingleton>.Instance;                           -
    end;                                                                        -
                                                                                -
                                                                               *)
    //---------------------------------------------------------------------------
     
    implementation
     
    const
      SINGLETON_ASSERT_CHECK_INSTANCE_FMT = 'Must Call property "Instance" of "%s" and not named constructor "Create"'; // Do not localize
      SINGLETON_ERRORS: array[ESLTSingletonError.TErrorType] of string = (SINGLETON_ASSERT_CHECK_INSTANCE_FMT);
     
    { ESLTSingletonError }
     
    //------------------------------------------------------------------------------
    constructor ESLTSingletonError.Create(ErrorType: TErrorType; ASingletonClass: TClass);
    begin
      CreateFmt(SINGLETON_ERRORS[ErrorType], [ASingletonClass.ClassName()]);
    end;
     
    { TSLTSingleton<T> }
     
    //------------------------------------------------------------------------------
    class procedure TSLTSingleton<T>.CheckInstance(Obj: T);
    begin
      if Assigned(FInstance) and Assigned(Obj) and (Obj <> FInstance) then
        raise ESLTSingletonError.Create(setCheckInstanceAssertion, Obj.ClassType()); // Pseudo-Assert Exception !
    end;
     
    //------------------------------------------------------------------------------
    class function TSLTSingleton<T>.Create(): T;
    begin
      Result := Instance;
    end;
     
    //------------------------------------------------------------------------------
    class destructor TSLTSingleton<T>.Destroy();
    begin
      FreeAndNil(FInstance);
    end;
     
    //------------------------------------------------------------------------------
    class function TSLTSingleton<T>.GetInstance(): T;
    begin
      if not Assigned(FInstance) then
        FInstance := T.Create();
     
      Result := FInstance;
    end;
     
    //------------------------------------------------------------------------------
    class procedure TSLTSingleton<T>.NotifyDestroy(Obj: T);
    begin
      if Assigned(FInstance) and (Obj = FInstance) then
        FInstance := nil;
    end;
     
    { TSLTSingletonThreadSafe<T> }
     
    //------------------------------------------------------------------------------
    class constructor TSLTSingletonThreadSafe<T>.Create();
    begin
      FInstanceLock := TCriticalSection.Create();
    end;
     
    //------------------------------------------------------------------------------
    class destructor TSLTSingletonThreadSafe<T>.Destroy();
    begin
      FInstanceLock.Acquire();
      try
        FInstance := nil; // On est pas responsable de cette libération
      finally
        FInstanceLock.Release();
      end;
     
      FreeAndNil(FInstanceLock);
    end;
     
    //------------------------------------------------------------------------------
    class function TSLTSingletonThreadSafe<T>.GetThreadSafeInstance(): T;
    begin
      // L'utilisation d'une Section critique (TCriticalSection) au lieu d'un TMultiReadExclusiveWriteSynchronizer
      // les plus :
      // - Plus léger (n'ajoute pas event+thread)
      // - Plus facile a gérer car on peut facilement simuler une promotion d'un Verrou en Lecture vers un Verrou en Ecriture,
      //   J'ai des souvenir que cette pratique peut engendrer un DeadLock !
      //   Après avoir fait quelques tests, je n'arrive pas à la reproduire mais je préfère éviter cela : "ID: 17761, TMultiReadExclusiveWriteSynchronizer deadlock fix-SysUtils.pas" - http://cc.embarcadero.com/Item/17761)
      // - Supporte l'imbrication sans trop de complexité
      // - Permet de conserver la "Lazy Initialization" typique du Singleton !
      // les moins :
      // - nuit quelque peu à la performance des Threads si on ne triche pas légèrement en lecture lors de la promotion vers un Verrou en Ecriture !
     
      // Si le pointeur existe, il n'y aucune raison de "sécuriser l'accès" qui ne sera qu'en lecture par la suite
      // Plusieurs threads peuvent lire cette instance (un seul peut l'écrire)
      if not Assigned(FInstance) then
      begin
        // la "Lazy Initialization" nécessite une protection
        FInstanceLock.Acquire();
        try
          // Si cela se trouve, le Acquire a attendu qu'un autre thread crée le Singleton !
          // Un thread doit toujours supprimer les échantillons précédents de la mémoire protégée après avoir transformé un verrou de lecture en verrou d'écriture
          if not Assigned(FInstance) then
            FInstance := GetInstance(); // Utilisation du Singleton non thread safe
        finally
          FInstanceLock.Release();
        end;
      end;
     
      Result := FInstance;
    end;
     
    end.
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [VxiR2] Variables globales et objets pour moyennes mobiles par mois glissant
    Par sfbertrand dans le forum Designer
    Réponses: 3
    Dernier message: 02/06/2009, 10h31
  2. Objet Connection variable global
    Par anikeh dans le forum Access
    Réponses: 6
    Dernier message: 23/08/2006, 19h37
  3. [C#] variables globales et bonne implémentation singleton
    Par grome dans le forum Windows Forms
    Réponses: 7
    Dernier message: 05/05/2006, 11h11
  4. Réponses: 5
    Dernier message: 25/05/2005, 22h29

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo