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

Contribuez Delphi Discussion :

Design Pattern : Singleton - Découverte des Génériques et Class Constructor


Sujet :

Contribuez Delphi

  1. #1
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    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 429
    Points : 24 794
    Points
    24 794
    Par défaut Design Pattern : Singleton - Découverte des Génériques et Class Constructor
    Depuis peu retour en Delphi !
    Après un passage en C++Buider 2007, XE2 et XE3
    Je reprends du Delphi XE2 et je découvre les nouveautés que je n'avais pas en Delphi 7 !

    M'étant habitué au Template du C++, je me suis lancé dans les génériques !

    Dans la société ou je travaille, j'ai trouvé une Pattern singleton utilisant les génériques, dommage que l'auteur d'origine Moritz Beutel n'ait pas été mentionné dans le code de mon prédécesseur

    De la façon qu'elle a été utilisé, il était d'une grande simplicité de créer plusieurs singletons !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    var
      BiduleSingleton : TSingletonInstance<TBidule>;
      BiduleDoublon : TSingletonInstance<TBidule>;
    BiduleSingleton et BiduleDoublon sont des variables globales, c'est la syntaxe non OO comme pour Printer()
    Dommage, il existe aujourd'hui les class property et class var autant s'en servir !

    Pour ma part, je préfère écrire dans mon TBidule.Instance.Propriété...

    De plus, il n'y a RIEN dans le code de TBidule que l'on doit l'utiliser comme Singleton, donc rien empêche que l'on fasse des Create !

    Au passage, j'ai donc expérimenté une nouveauté pour moi, les class constructor !
    Et l'Astuce pour masquer le constructeur Create en m'inspirant de la proposition de yanniel !

    Voici le code de la Pattern (j'ai prévu d'en faire d'autres)

    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
     
    unit SLT.Common.DesignPattern;
     
    interface
     
    uses System.SysUtils;
     
    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
        constructor Create(ErrorType: TErrorType; ASingletonClass: TClass);
      end;
     
      /// <summary>Aide pour la création d'une classe respectant le Patron de Conception "Singleton"</summary>
      TSLTSingleton<T: class, constructor> = class(TObject)
      private
        class var
          FInstance: T;
      protected
        class function GetInstance(): T; static;
        class procedure CheckInstance(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>Classe 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>)
     
      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;
     
    end.

    une Utilisation simpliste :

    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
    type
      TBiduleSingleton : TSingletonInstance<TBidule>;
      TBiduleFauxDoublon : TSingletonInstance<TBidule>;
     
    constructor TBidule.Create();
    begin
      inherited Create();
     
      TBiduleSingleton.CheckInstance(Self);
      TBiduleFauxDoublon.CheckInstance(Self);
     
      if TBiduleSingleton.Create() = TBiduleSingleton.Instance then
        Beep();
     
      if TBiduleFauxDoublon.Instance = TBiduleSingleton.Instance then
        Beep();
    end;
    Comme c'est au niveau de la classe que cela gère l'instance du Singleton, il est plus difficile de le contourner en jouant juste sur plusieurs déclarations du même template
    Mais rien n'empêche d'appeler le constructor Create de TBidule !

    De plus, la syntaxe est pour le moment
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TBiduleSingleton.Instance.Propriété
    , il est possible de faire une encapsulation dans TBidule pour obtenir
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TBidule.Instance.Propriété
    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
     
    type
      TBidule = class(TObject)
      private
        type
          TBiduleSingleton = TSLTSingleton<TBidule>;
      public
        // Constructeurs d'Instance
        constructor Create();
        destructor Destroy(); override;
        /// <summary>Singleton : Point d'Accès unique du TBidule</summary>
        class function GetInstance(): TBidule; static;
     
        // Propriétés de Classe
        class property Instance: TBidule read GetInstance;
      public
    end;
     
    //------------------------------------------------------------------------------
    constructor TBidule.Create;
    begin
      TBiduleSingleton.CheckInstance(Self);
     
      // Les Initialisations de TBidule
    end;
     
    //------------------------------------------------------------------------------
    destructor TBidule.Destroy;
    begin
      // Les Libérations de TBidule
    end;
     
    //------------------------------------------------------------------------------
    class function TBidule.GetInstance: TBidule;
    begin
      Result := TBiduleSingleton.Instance;
    end;
    Voilà, cela ne sert pas à grand chose mais cela m'a fait découvrir des éléments inconnus
    Je trouve le class constructor et class destructor plus élégant des sections initialization et finalization
    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

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    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 429
    Points : 24 794
    Points
    24 794
    Par défaut
    Voici la version Thread Safe !

    A Savoir que peu importe le nombre de type que l'on déclare c'est toujours la même classe donc la même class var

    Par contre, il est du coup possible de contourner le Thread Safe !
    Ayant prévu de toujours encaspuler le TSingletonInstance à l'intérieur de class qu'il gère, je n'aurais pas ce soucis !

    De plus, mes classes gérées en Singleton seront marquées en sealed, j'ai pris cette habitude du [[final]] c++0x sauce ER pour ce genre de classe !
    Si je dois avoir un Singleton redéfinissable, je préfère passé par un Multiton couplé à une Factory (couplé même à une Class Registry)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type
      TBiduleSingleton : TSingletonInstance<TBidule>;
      TBiduleFauxDoublon : TSingletonInstance<TBidule>;
      TBiduleSingletonThreadSafe : TSLTSingletonThreadSafe<TBidule>;
      TBiduleFauxDoublonThreadSafe : TSLTSingletonThreadSafe<TBidule>;
    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
    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
        constructor Create(ErrorType: TErrorType; ASingletonClass: TClass);
      end;
     
      /// <summary>Aide pour la création d'une classe respectant le Patron de Conception "Singleton"</summary>
      TSLTSingleton<T: class, constructor> = class(TObject)
      private
        class var
          FInstance: T;
      protected
        class function GetInstance(): T; static;
        class procedure CheckInstance(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>Classe 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;
     
     
    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;
     
    { TSLTSingletonThreadSafe<T> }
     
    //------------------------------------------------------------------------------
    class constructor TSLTSingletonThreadSafe<T>.Create();
    begin
      FInstanceLock := TCriticalSection.Create();
    end;
     
    //------------------------------------------------------------------------------
    class destructor TSLTSingletonThreadSafe<T>.Destroy();
    begin
      FInstance := nil; // On est pas responsable de cette libération
     
      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 légèrement en lecture
     
      // 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 !
          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

  3. #3
    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 430
    Points
    28 430
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Je trouve le class constructor et class destructor plus élégant des sections initialization et finalization
    à mes yeux ce n'est pas la même chose

    par exemple, dans une unité Socket, initialization permettra sous Windows d'appeler WSAStartup, appel qui doit être fait systématiquement et une seule fois dans le projet.

    le class constructor lui est comme son nom l'indique lié à une classe. Pour rester dans l'API Win32 on pourrait imaginer le class constructor d'un objet fenêtre pour déclarer la classe avec RegisterClass (celui de Windows) alors que le constructor appelera CreateWindow pour obtenir un HWnd...

    Attention aussi à ces deux remarques:
    Note: Even though the compiler takes care of ordering the initialization of classes, in some complex scenarios, ordering may become random. This happens when the class constructor of a class depends on the state of another class that, in turn, depends on the first class.
    Note: The class constructor for a generic class or record may execute multiple times. The exact number of times the class constructor is executed in this case depends on the number of specialized versions of the generic type. For example, the class constructor for a specialized TList<String> class may execute multiple times in the same application.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  4. #4
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    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 429
    Points : 24 794
    Points
    24 794
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    Citation Envoyé par ShaiLeTroll Voir le message
    Je trouve le class constructor et class destructor plus élégant des sections initialization et finalization
    Dingue ! Tu as lu jusqu'au bout (ou tu as commencé par la fin )

    Je découvre, je faisais confiance à l'aide
    Même si le même résultat peut être obtenu en plaçant le code d'initialisation de la classe dans la section initialization, les constructeurs de classes ont l'avantage d'aider le compilateur à déterminer les classes qui doivent être incluses dans le fichier binaire final et celles qui doivent être retirées.

    par exemple, dans une unité Socket, initialization permettra sous Windows d'appeler WSAStartup, appel qui doit être fait systématiquement et une seule fois dans le projet.
    C'est intéressant, j'avais une application qui utilisait une fonction GetLocalIP similaire à la FAQ Comment connaître son adresse IP !
    Et cela plusieurs fois !

    En plus comment gérer le fait que plusieurs unités genre Indy, ICS, ScktComp ... fasse toute le WSAStartup chacune dans leur initialization



    le class constructor lui est comme son nom l'indique lié à une classe.
    Pour rester dans l'API Win32 on pourrait imaginer le class constructor d'un objet fenêtre pour déclarer la classe avec RegisterClass (celui de Windows) alors que le constructor appelera CreateWindow pour obtenir un HWnd...
    Je n'ai rien compris !
    Figure toi que je pensais au début que le class constructor était systématique et en fait il n'est fait que si le code utilise la classe en faisant une instance ou en utilisant une méthode de classe (mon cas maintenant)

    Je pensais justement faire une Class Registry en passant par class constructor !
    Evidemment, cela ne fonctionne pas !
    Là j'ai fait un choix, je ne voulais pas que l'ajout de l'unité provoque le Register implictement comme par exemple PngImage ou JpegImage
    Alors, dans le DPR, j'appel un Register explicite !
    Pour le moment, c'est un programme de test, je vais réfléchir à cela plus tard !

    Je t'avoue ne pas voir la différence entre initialization et finalization d'une unité et le class constructor et class destructor !
    A part que c'est effectivement lié à une seule classe et non à l'unité !
    Et que c'est subtilement optimisé !
    Je trouve que cela fait plus OO de pouvoir instancier mes class var depuis un class constructor et de les libérer depuis un class destructoir que l'époque de D7 ou je faisais une variable globale pseudo privée et initialization et finalization ou alors mon #pragma exit en C++Builder qui était une fonction qui au final appelait une méthode static de mes classes pour libérer les singletons et multitons !

    Citation Envoyé par Paul TOTH Voir le message
    Attention aussi à ces deux remarques:
    Oui, l'ordre ne m'importe peu !
    J'ai déjà été piégé en D6 avec le initialization qui change d'ordre selon si l'on fait un simple compiler ou un construire
    L'initialization de l'unit fraichement recompilé est en dernier, j'avais codé ma globale comme TApplication, mauvaise idée !
    C'est pour cela que j'avais justement essayé d'utiliser des singletons en D6 pour gérer les instanciations plus souplement que par initialization

    Pour les génériques, selon mes tests avec 3 classes "singletonées", cela m'appel pour chaque variante de singleton et me libère chacune des mes instances comme prévu !
    C'est même le comportement que j'espérais en lisant ces lignes ce matin (en VF pour ma part dans mon ms-help local)
    Et Je peux faire autant d'alias cela n'appel qu'une fois car il n'a bien généré qu'une seule variante même si l'on en duplique le type
    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

  5. #5
    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 430
    Points
    28 430
    Par défaut
    oui WSAStartup, une seule fois suffit, mais tu peux l'appeler plusieurs fois

    le class constructor présente l'avantage de n'être invoqué et intégré au projet compilé QUE si la classe est utilisée.

    l'exemple que je donnais est lié à l'API Windows dans laquelle tu utilise RegisterClass() pour définir un classe de fenêtre avec sa méthode WndProc, ses attributs, puis avec CreateWindow() tu instancies une fenêtre de cette classe. On a donc bien une similitude avec le "class constructor" qui va faire une initialisation unique pour toutes les instances à venir et le "constructor" qui s'intéresse à chaque instance en particulier.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  6. #6
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    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 429
    Points : 24 794
    Points
    24 794
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    le class constructor présente l'avantage de n'être invoqué et intégré au projet compilé QUE si la classe est utilisée..
    Oui, ce fut l'effet de bord que je n'avais pas bien compris !
    Mon unité Auto-utilisait la classe (un RegisterClass maison), j'aurais pensé cela suffisant !
    Après le reste étant gérant par des interfaces\support... on a avait jamais d'appel explicite en dehors !

    Et finalement, je trouve cela TRES élégant !
    Initialization avec trop de code implicite rendait mes Class Registry un peu difficile à comprendre parce que le simple faite d'avoir l'unité dans le projet suffisait pour activer des mécanismes dans certaines applications

    Maintenant, j'ai clairement et explicitement un RegisterClass maison appelé pour les classes que je prévois d'utiliser ensuite dans le projet qui fournissent différentes formes d'implémentation des mêmes interfaces

    Citation Envoyé par Paul TOTH Voir le message
    l'exemple que je donnais est lié à l'API Windows ....
    Ah oui, je n'ai jamais testé le Windows::RegisterClass ni Windows::CreateWindow !
    Je connais de nom suite à la lecture du bouquin Borland C++ 5.0 de Gérard Leblanc, rien de plus !

    Mais cela correspond exactement à mon besoin, un peu comme si je préparais mes MetaData puis mes Data

    Et pour mes différents singletons thread safe ou pas, les class destructors nettoyent tout ce qu'il faut !
    J'utilise OutputDebugString et ReportMemoryLeaksOnShutdown pour bien comprendre ce qui se passe !
    L'Article Variable classe dans les génériques confirme cette multiplicité des class var selon la variante typée

    La phrase suivante (juste VF de ta citation) :

    Remarque : Le constructeur de classe pour un enregistrement ou une classe générique peut s'exécuter plusieurs fois. Le nombre exact de fois que le constructeur de classe est exécuté dans ce cas dépend du nombre de versions spécialisées du type générique. Par exemple, le constructeur de classe pour une classe TList<String> spécialisée peut s'exécuter plusieurs fois dans la même application.
    classe TList<String> spécialisée que doit-on comprendre ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    type
      TSingle1 = TSLTSingleton<TBidule>;
      TSingle2 = TSLTSingleton<TBidule>;
      TSingle3 = class(TSLTSingleton<TBidule>);
      TSingle4 = class(TSLTSingleton<TBidule>)
        procedure toto;
      end;
    Est-ce plusieurs spécialisation de TSLTSingleton<TBidule> ?
    Personnellement, j'aurais dis NON
    Mais Il semble que OUI, je pensais qu'il fallait un type différent pour considérer que c'était une spécialisation différente

    Je comprends pas du tout cette formule classe TList<String> spécialisée !

    Je n'ai qu'une seule fois class destructor de TSLTSingleton !

    En C++, je pensais que la spécialisation de Template c'était lorsque l'on ajoutait du code parce que certaines parties ne pouvait pas être généralisé, voir Template et Héritage
    Je ne mettais pas de nom sur le simple faite de "typer" le Template, peut-être cela s'appelle aussi spécialisation !

    En Delphi, je n'ai pas poussé encore les Génériques assez loin pour comprendre la véritable spécialisation !

    et le TSLTSingleton n'aura JAMAIS besoin de code spécialisé à mon avis !

    Par contre si j'ajoute
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TSingleAutre = TSLTSingleton<TMachin>;
    J'ai bien deux class destructor !

    Si j'ajoute
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TSingleAutre2 = TSLTSingletonThreadSafe<TMachin>;
    Toujours deux class destructor TSLTSingleton et un pour Thread Safe

    Si j'ajoute
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TSingleAutre3 = TSLTSingletonThreadSafe<TMachinBidule>;
    3 class destructor TSLTSingleton et un pour Thread Safe

    Donc totalement logique !
    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

  7. #7
    Membre actif
    Avatar de Eric.H
    Homme Profil pro
    Inscrit en
    Décembre 2004
    Messages
    220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Décembre 2004
    Messages : 220
    Points : 286
    Points
    286
    Par défaut
    Merci à vous deux... intéressant.

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

Discussions similaires

  1. Les design pattern pour créer des jeux
    Par alex6891 dans le forum Design Patterns
    Réponses: 4
    Dernier message: 26/11/2018, 20h59
  2. scope application et design pattern singleton
    Par totoche dans le forum Servlets/JSP
    Réponses: 1
    Dernier message: 01/10/2008, 16h56
  3. [Singleton] Classe static ou Design Pattern Singleton ?
    Par piloupy dans le forum Design Patterns
    Réponses: 15
    Dernier message: 01/08/2008, 17h04
  4. Réponses: 1
    Dernier message: 04/07/2008, 15h53
  5. Implémentation du design pattern singleton
    Par 0pierrot0 dans le forum C++
    Réponses: 1
    Dernier message: 22/01/2008, 11h01

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