+ Répondre à la discussion Actualité déjà publiée
Page 1 sur 2 12 DernièreDernière
  1. #1
    Responsable Pascal, Delphi et Assembleur


    Avatar de Alcatîz
    Homme Profil pro
    Ressources humaines
    Inscrit en
    mars 2003
    Messages
    7 317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ressources humaines
    Secteur : Service public

    Informations forums :
    Inscription : mars 2003
    Messages : 7 317
    Points : 50 916
    Points
    50 916
    Billets dans le blog
    2

    Par défaut Tutoriel : Implémentation du Singleton avec Delphi 7, par Jérémy Laurent

    Implémentation du Singleton avec Delphi 7, par Jérémy Laurent
    Premier article d'une série consacrée aux patrons de conception (design patterns)

    Jérémy Laurent a décidé de présenter l'implémentation de différents patrons de conception (design patterns) avec Delphi. Le premier d'entre eux est le « Singleton », qui est un modèle visant à limiter l'instanciation d'une classe à un seul et unique objet. Il est couramment utilisé pour coordonner des opérations dans un système.

    Cette implémentation ne pose aucun problème sur les dernières versions de Delphi ; par contre, avec les versions plus anciennes, les champs statiques ne sont pas autorisés et provoquent une erreur de compilation. Cet article présente différentes solutions.

    http://jeremy-laurent.developpez.com...rns/singleton/

    Qu'en pensez-vous ?
    Règles du forum
    Cours et tutoriels Pascal, Delphi, Lazarus et Assembleur
    Avant de poser une question, consultez les FAQ Pascal, Delphi, Lazarus et Assembleur
    Mes tutoriels et sources Pascal

    Le problème en ce bas monde est que les imbéciles sont sûrs d'eux et fiers comme des coqs de basse cour, alors que les gens intelligents sont emplis de doute. [Bertrand Russell]

  2. #2
    Membre expérimenté
    Avatar de ALWEBER
    Homme Profil pro
    Inscrit en
    mars 2006
    Messages
    865
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations forums :
    Inscription : mars 2006
    Messages : 865
    Points : 1 322
    Points
    1 322
    Billets dans le blog
    4

    Par défaut Apport sous Delphi XE

    Lorsque l'on définit un objet par une description de type Class ou équivalent, celui-ci peut être utilisé au travers d'une variable de ce type d'objet. L'objet est alors instancié avec le constructor Create.
    Lorsque l'objet défini contient des Class Var, Class Property, Class Function, etc. une instance unique de l'objet est automatiquement créée au lancement du programme. L'accès à l'un de ces Class éléments ce fait en préfixant l'élement par le type de la classe. Même si les Class éléments peuvent cohabiter avec d'autres élément dans la description d'une classe, les Class éléments ne peuvent pas communiquer avec les autres éléments (et réciproquement). Voir l'exemple ci-dessous.

    En réalité Delphi travaille sur deux instances différentes de classes comme illustré dans le document ci joint. Voilà un moyen simple de travailler sur un singleton (cf Design Pattern).

    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
     
    . . .
    type
      TDemo = class
      private
        class var fV2 : integer ;
      public
        V1 : integer ;
        class property ClassV2 : integer read fv2 write fv2 ;
        function func1 : string  ;
        class function classfunc2 : string  ;
        class function classfunc3 : string  ;
      end;
     
    var
      aDemo : TDemo ;
     
    function TDemo.func1: string;
    begin
      v1 := integer(pointer(self)) ;
      fv2 := fv2 + 1 ;
      result := intToHex (V1,8)
    end;
     
    class function TDemo.classfunc2: string;
    begin
      fv2 := integer(pointer(self)) ;
      result := intToHex (fv2,8)
    end;
     
    class function TDemo.classfunc3: string;
    begin
      fv2 := fv2 + 1 ;
      result := intToHex (ClassV2,8) ;
    end;
     
     
    begin
      writeln (TDemo.classfunc3) ;
      writeln (TDemo.classfunc3) ;
      aDemo := TDemo.Create ;
      writeln (aDemo.func1) ;
      writeln (aDemo.classfunc3) ;
      writeln (aDemo.classfunc3) ;
      writeln (aDemo.classfunc2) ;
      FreeAndNil (aDemo) ;
      writeln (TDemo.classfunc2) ;
      writeln (intToHex(TDemo.ClassV2,8)) ;
      readln ;
    end.

  3. #3
    Rédacteur/Modérateur
    Avatar de Andnotor
    Profil pro
    Inscrit en
    septembre 2008
    Messages
    4 346
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : septembre 2008
    Messages : 4 346
    Points : 8 776
    Points
    8 776

    Par défaut

    Citation Envoyé par ALWEBER Voir le message
    Lorsque l'objet défini contient des Class Var, Class Property, Class Function, etc. une instance unique de l'objet est automatiquement créée au lancement du programme.
    Non, il n'y a pas d'instance, c'est directement la déclaration de la classe qui est altérée.

    Citation Envoyé par ALWEBER Voir le message
    Même si les Class éléments peuvent cohabiter avec d'autres élément dans la description d'une classe, les Class éléments ne peuvent pas communiquer avec les autres éléments (et réciproquement).
    Le réciproquement est faux. L'instance sait où se trouve la déclaration de la classe. Tu l'illustres d'ailleurs dans func1, fv2 est bien incrémenté.

    Citation Envoyé par ALWEBER Voir le message
    En réalité Delphi travaille sur deux instances différentes de classes...
    Non

    Ton code n'est pas bon dans le sens où tu te réfères toujours à Self mais Self est l'instance dans un cas et la classe dans l'autre. Ajoute cette autre fonction...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    function TDemo.func2: string;
    begin
      v1 := integer(ClassType) ;
      result := intToHex (V1,8)
    end;
    ...et tu verras qu'elle renvoie bien le même pointeur que classfunc2.

  4. #4
    Membre expérimenté
    Avatar de ALWEBER
    Homme Profil pro
    Inscrit en
    mars 2006
    Messages
    865
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations forums :
    Inscription : mars 2006
    Messages : 865
    Points : 1 322
    Points
    1 322
    Billets dans le blog
    4

    Par défaut Réponse

    Remarque sur la notion d'instance : J'ai choisi d'utiliser la notion d'instance plutôt que 'déclaration de la classe' car dans le cas qui est présenté la variable fV2 est bien localisée dans cet espace mémoire (voir @fv2) et son contenu est modifié à cet endroit. Je tiens compte de ta remarque sur la réciprocité. En effet on y accède via ClassType (PPointer(Self)^))

    Je reprendrais mon texte de la manière suivante :

    Lorsque l'on définit un objet par une description de type Class ou équivalent, celui-ci peut être utilisé au travers d'une variable de ce type d'objet. L'objet est alors instancié avec le constructor Create.
    Une première instance de classe est automatiquement créée au lancement du programme et sert de déclaration de la classe. Toute instance ultérieure créée ne contient que les éléments non "class élément"
    Lorsque l'objet défini contient des Class Var, Class Property, Class Function, etc. L'accès à l'un de ces Class éléments ce fait en préfixant l'élement par le type de la classe. Même si les Class éléments peuvent cohabiter avec d'autres élément dans la description d'une classe, les Class éléments ne peuvent pas communiquer avec les autres éléments. Dans l'exemple ci-dessous classfunc2 ne peut pas utiliser v1.

  5. #5
    Membre expérimenté
    Avatar de ALWEBER
    Homme Profil pro
    Inscrit en
    mars 2006
    Messages
    865
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations forums :
    Inscription : mars 2006
    Messages : 865
    Points : 1 322
    Points
    1 322
    Billets dans le blog
    4

    Par défaut Remarque 2

    Le générateur de Pattern dans Delphi XE pour le Singleton conduit maintenant à ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
      TSingleton = class
      strict private
        class var FInstance: TSingleton;
        constructor Create;
      public
        class function GetInstance: TSingleton;
      end;
    pour prévenir d'une éventuelle surcharge du constructor

  6. #6
    Membre expérimenté
    Avatar de ALWEBER
    Homme Profil pro
    Inscrit en
    mars 2006
    Messages
    865
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations forums :
    Inscription : mars 2006
    Messages : 865
    Points : 1 322
    Points
    1 322
    Billets dans le blog
    4

    Par défaut Pour ouvrir le débat en Delphi 7

    Ci joint un petit test en Delphi 7

    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
     
    program P1 ;
    {$APPTYPE CONSOLE}
    {$R *.res}
    uses
      Windows,  SysUtils,  Classes;
     
    type
      TDemo = class
      public
        V1 : integer ;
        function func1 : string  ;
      end;
     
    { TDemo }
     
    function TDemo.func1: string;
    begin
      result := intToHex (v1,8) ;
    end;
     
    var
      aDemo : TDemo ;
     
    begin
      aDemo := TDemo( aDemo.ClassType) ;
      aDemo.v1 := 3 ;
      writeln (aDemo.v1) ;
      writeln (aDemo.func1) ;
      readln ;
    end.

  7. #7
    Rédacteur/Modérateur
    Avatar de Andnotor
    Profil pro
    Inscrit en
    septembre 2008
    Messages
    4 346
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : septembre 2008
    Messages : 4 346
    Points : 8 776
    Points
    8 776

    Par défaut

    Citation Envoyé par ALWEBER Voir le message
    Remarque sur la notion d'instance : J'ai choisi d'utiliser la notion d'instance plutôt que 'déclaration de la classe' car dans le cas qui est présenté la variable fV2 est bien localisée dans cet espace mémoire
    Oui mais c'est faux de parler d'instance. Une instance est une allocation dynamique en fonction d'une déclaration.

    Citation Envoyé par ALWEBER Voir le message
    Une première instance de classe est automatiquement créée au lancement du programme et sert de déclaration de la classe.
    Et comment cela serait-il possible ? Imagine simplement que le constructeur attende un certain nombre de paramètres, comment ceux-ci pourraient-ils être évalués ?
    La déclaration de la classe est en mémoire, oui ! Mais c'est le programme après tout ! Pour illustrer cela, je te propose une autre petit démo :

    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
      TDemo = class
        procedure Test;
      end;
     
    var
      Demo :TDemo = nil;
     
    procedure TDemo.Test;
    begin
      ShowMessage('Texte');
    end;
     
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Demo.Test;
    end;
    Il n'y a aucune instance de TDemo qui est créée, mais ça fonctionne ! Le code est là et exécutable et pourtant il n'y a pas de class var, class function, etc.

    Ajoutons maintenant une variable qui elle devrait être allouée (donc y avoir un Create) :
    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
    type
      TDemo = class
        Value :string;
        procedure Test;
      end;
     
    var
      Demo :TDemo = nil;
     
    procedure TDemo.Test;
    begin
      ShowMessage(Value);
    end;
     
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Demo.Test;
    end;
    Nous obtenons une belle violation d'accès. Normal !
    Peut-on tester s'il y a bien eu allocation ? Oui, par Self :

    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
    type
      TDemo = class
        Value :string;
        procedure Test;
      end;
     
    var
      Demo :TDemo = nil;
     
    procedure TDemo.Test;
    begin
      if Self <> nil then
        ShowMessage(Value);
    end;
     
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Demo.Test;
    end;
    Pas de ShowMessage, mais plus de VA

    En bref le code est toujours là et exécutable et ce n'est que Self (un pointeur sur les données propres à cette instance) qui est déterminant.

    Citation Envoyé par ALWEBER Voir le message
    Lorsque l'objet défini contient des Class Var, Class Property, Class Function, etc. L'accès à l'un de ces Class éléments ce fait en préfixant l'élement par le type de la classe.
    A nouveau c'est faux et à nouveau tu prouves le contraire dans ton exemple en appelant une fois TDemo.classfunc2 et l'autre aDemo.classfunc2. Mais puisque c'est une méthode de classe Self représente ici toujours la classe et non l'instance aDemo. La seule méthode de classe où Self représente l'instance est... le constructeur

  8. #8
    Rédacteur/Modérateur
    Avatar de Andnotor
    Profil pro
    Inscrit en
    septembre 2008
    Messages
    4 346
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : septembre 2008
    Messages : 4 346
    Points : 8 776
    Points
    8 776

    Par défaut

    Citation Envoyé par ALWEBER Voir le message
    Le générateur de Pattern dans Delphi XE pour le Singleton conduit maintenant à ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
      TSingleton = class
      strict private
        class var FInstance: TSingleton;
        constructor Create;
      public
        class function GetInstance: TSingleton;
      end;
    pour prévenir d'une éventuelle surcharge du constructor
    Ce code n'empêche absolument pas un TSingleton.Create mais c'est TObject.Create qui sera appelé uniquement. Quant à la surcharge, et bien c'est aussi TObject.Create qui le sera ! Il n'est pas possible de réduire la visibilité/portée d'une méthode/propriété.

    Quant au singleton, je me demande si on ne pourrait pas simplement l'écrire ainsi (et pardon à Jérémy d'avoir un peu dévié du sujet) :
    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
    type
      TSingleton = class
      strict private
        class var FInstance: TSingleton;
      public
        class function Get: TSingleton;
        constructor    Create;
        destructor     Destroy; override;
      end;
     
    constructor TSingleton.Create;
    var
      Ptr :pointer;
    begin
      if Assigned(FInstance) then
      begin
        //Libère l'objet en cours de création et
        //le remplace par l'instance existante
        Ptr := FInstance;
        Destroy;
        Self := Ptr;
      end;
     
      FInstance := Self;
    end;
     
    destructor TSingleton.Destroy;
    begin
      FInstance := nil;
      inherited;
    end;
     
    class function TSingleton.Get: TSingleton;
    begin
      Result := Create;
    end;
    ainsi appeler plusieurs fois le Create renverrait aussi l'instance existante. Après quelques essais, ça semble être le cas (et aucun Memory leak détecté).

  9. #9
    Rédacteur/Modérateur
    Avatar de Andnotor
    Profil pro
    Inscrit en
    septembre 2008
    Messages
    4 346
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : septembre 2008
    Messages : 4 346
    Points : 8 776
    Points
    8 776

    Par défaut

    Citation Envoyé par ALWEBER Voir le message
    Ci joint un petit test en Delphi 7

    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
     
    program P1 ;
    {$APPTYPE CONSOLE}
    {$R *.res}
    uses
      Windows,  SysUtils,  Classes;
     
    type
      TDemo = class
      public
        V1 : integer ;
        function func1 : string  ;
      end;
     
    { TDemo }
     
    function TDemo.func1: string;
    begin
      result := intToHex (v1,8) ;
    end;
     
    var
      aDemo : TDemo ;
     
    begin
      aDemo := TDemo( aDemo.ClassType) ;
      aDemo.v1 := 3 ;
      writeln (aDemo.v1) ;
      writeln (aDemo.func1) ;
      readln ;
    end.
    Là, je ne vois pas vraiment où tu veux en venir et le rapport avec un singleton

    Il n'y a pas de création, c'est fichu d'avance. Si tu pensais à aDemo := TDemo(aDemo.ClassType).Create ça n'ira pas non plus puisque ClassType n'est pas une méthode de classe, qu'elle se réfère à Self (toujours lui) et qu'il vaut nil à cet instant.

    Et si on voulant se baser sur une instance existante pour en créer une nouvelle, l'écriture serait ainsi pointer(Demo2) := Demo1.ClassType.Create;.

  10. #10
    Membre expérimenté
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    mars 2005
    Messages
    982
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : mars 2005
    Messages : 982
    Points : 1 492
    Points
    1 492

    Par défaut

    Citation Envoyé par Andnotor
    Quant au singleton, je me demande si on ne pourrait pas simplement l'écrire ainsi (et pardon à Jérémy d'avoir un peu dévié du sujet)
    Il n'y a pas de mal.
    Citation Envoyé par Andnotor
    :
    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
    type
      TSingleton = class
      strict private
        class var FInstance: TSingleton;
      public
        class function Get: TSingleton;
        constructor    Create;
        destructor     Destroy; override;
      end;
     
    constructor TSingleton.Create;
    var
      Ptr :pointer;
    begin
      if Assigned(FInstance) then
      begin
        //Libère l'objet en cours de création et
        //le remplace par l'instance existante
        Ptr := FInstance;
        Destroy;
        Self := Ptr;
      end;
     
      FInstance := Self;
    end;
     
    destructor TSingleton.Destroy;
    begin
      FInstance := nil;
      inherited;
    end;
     
    class function TSingleton.Get: TSingleton;
    begin
      Result := Create;
    end;
    ainsi appeler plusieurs fois le Create renverrait aussi l'instance existante. Après quelques essais, ça semble être le cas (et aucun Memory leak détecté).
    Je n'ai pas encore eu l'occasion de travailler avec XE. Si j'en ai le loisir, j'essaierai.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Ptr := FInstance;
    Destroy;
    Self := Ptr;
    C'est assez bourrin comme méthode !

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

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : juillet 2006
    Messages : 10 828
    Points : 17 981
    Points
    17 981

    Par défaut

    A la base le Tutoriel concerne "Singleton dans une version de Delphi qui ne supporte pas les champs statiques" comme l'est Delphi 7 !

    Evoquer XE est hors sujet comme le souligne AndNotOr !
    Avec le class var, créer un Singleton peut se faire comme en Java ou C#, ce n'est pas un grand défi !

    Sous Delphi 7, ce n'est pas difficile mais demande un peu plus d'astuce comme le démontre parfaitement le tutoriel de Popo

    D'ailleurs, le tutoriel évoque NewInstance pour gérer la création d'instance !

    Il existe plein de générique pour créer un Singleton en exemple sur le net,
    dont le mien Design Pattern : Singleton - Découverte des Génériques et Class Constructor et ses classes TSLTSingleton et TSLTSingletonThreadSafe

    Ainsi pas besoin de réécrire le même code de gestion du singleton à chaque fois !
    Obligatoire sous D7 à cause de l'ancienneté de la version mais on peut faire mieux sous XE... !
    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

  12. #12
    Rédacteur/Modérateur
    Avatar de Andnotor
    Profil pro
    Inscrit en
    septembre 2008
    Messages
    4 346
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : septembre 2008
    Messages : 4 346
    Points : 8 776
    Points
    8 776

    Par défaut

    Citation Envoyé par popo Voir le message
    C'est assez bourrin comme méthode !
    Pas faux ! NewInstance, je n'y avais pas pensé et est beaucoup mieux (éviter l'allocation purement et simplement)

  13. #13
    Membre éprouvé Avatar de guillemouze
    Profil pro
    Inscrit en
    novembre 2004
    Messages
    782
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : novembre 2004
    Messages : 782
    Points : 1 241
    Points
    1 241

    Par défaut

    Merci pour ce tutoriel Jérémy.
    Jusqu'à présent, j'utilisais la variable globale dans l'implémentation + class function, mais la paire NewInstance/FreeInstance me semble juste infiniment mieux, surtout pour éviter les Free intempestifs

  14. #14
    Rédacteur/Modérateur
    Avatar de Andnotor
    Profil pro
    Inscrit en
    septembre 2008
    Messages
    4 346
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : septembre 2008
    Messages : 4 346
    Points : 8 776
    Points
    8 776

    Par défaut

    Juste une petite remarque sur les codes proposés : Par fonction globale et héritage, il faut initialiser HiddenOnlineHelpConfiguration à nil sinon risque de VA

  15. #15
    Membre expérimenté
    Avatar de ALWEBER
    Homme Profil pro
    Inscrit en
    mars 2006
    Messages
    865
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations forums :
    Inscription : mars 2006
    Messages : 865
    Points : 1 322
    Points
    1 322
    Billets dans le blog
    4

    Par défaut

    Après quelques essais sur les anciennes versions de Delphi et quelques impasses j'ai trouvé cet article "Creating a real singleton class in Delphi 5"
    http://edn.embarcadero.com/article/22576

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

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : juillet 2006
    Messages : 10 828
    Points : 17 981
    Points
    17 981

    Par défaut

    Intéressant l'article sur l'EDN
    C'est ni plus ni moins l'approche du TInterfacedObject mais gérer au niveau de la classe au lieu de l'instance via les variables globales
    le principal inconvénient de cette approche, c'est qu'il faut libérer explicitement le singleton après chaque utilisation pour faire baisser le compteur de référence
    il manque le ThreadSafe avec l'InterlockIncrement


    C'est la technique qu'exploite indirectement via une Factory (le Provider) dans la variante avancée à base de ISingleton où le compteur de référence est intégré au TInterfacedObject
    C'est le Provider qui force une vie "éternelle" en conservant une référence sur l'interface
    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

  17. #17
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    novembre 2002
    Messages
    6 755
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : novembre 2002
    Messages : 6 755
    Points : 21 577
    Points
    21 577

    Par défaut

    allez, j'ajoute mes 2 cents, si on veux un vrai singleton il faudrait aussi une classe figée sinon rien ne m'empêche de surcharger ce qui est mis en place et sealed n'est pas supporté sous D7 si mon souvenir est bon.

    après moi j'aime bien cette écriture du singleton qui fonction avec toutes les versions

    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
     
    unit MySingleton;
     
    interface
     
    var
      PublicValue: Integer;
     
    procedure MaMethod;
     
    function GetPrivateValue: Integer;
     
    implementation
     
    var
      PrivateValue: Integer;
     
    function GetPrivateValue: Integer;
    begin
      Result := PrivateValue;
    end;
     
    procedure MaMethod;
    begin
    end;
     
    end.
    et ça s'utilise comme ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    uses
      MySingleton;
     
    begin
      MySingleton.PublicValue := 1;
      MySingleton.GetPrivateValie;
      MySingleton.MaMethod();
    end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Produits : UPnP, RemoteOffice, FlashPascal

  18. #18
    Membre expérimenté
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    mars 2005
    Messages
    982
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : mars 2005
    Messages : 982
    Points : 1 492
    Points
    1 492

    Par défaut

    Citation Envoyé par Andnotor Voir le message
    Juste une petite remarque sur les codes proposés : Par fonction globale et héritage, il faut initialiser HiddenOnlineHelpConfiguration à nil sinon risque de VA
    Tu es sûr ?
    Je n'ai jamais constaté ça.
    J'ai eu quelque souci avec la version avancée si je n'initialisais pas à nil mais pas avec la fonction globale (en même temps ça fait longtemps que je ne l'utilise plus), ni avec l'héritage.
    Citation Envoyé par ShaiLeTroll
    Intéressant l'article sur l'EDN
    C'est ni plus ni moins l'approche du TInterfacedObject mais gérer au niveau de la classe au lieu de l'instance via les variables globales
    le principal inconvénient de cette approche, c'est qu'il faut libérer explicitement le singleton après chaque utilisation pour faire baisser le compteur de référence
    il manque le ThreadSafe avec l'InterlockIncrement


    C'est la technique qu'exploite indirectement via une Factory (le Provider) dans la variante avancée à base de ISingleton où le compteur de référence est intégré au TInterfacedObject
    C'est le Provider qui force une vie "éternelle" en conservant une référence sur l'interface
    J'avais complètement oublié ce comportement de TInterfacedObject. C'est devenu tellement machinal...
    Comme précisé dans le tuto, ce que je trouvais le plus intérressant avec cette méthode, c'est que je n'avais pas à me me soucier de libérer un objet en l'utilisant.

  19. #19
    Membre expérimenté
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    mars 2005
    Messages
    982
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : mars 2005
    Messages : 982
    Points : 1 492
    Points
    1 492

    Par défaut

    Citation Envoyé par Paul TOTH Voir le message
    allez, j'ajoute mes 2 cents, si on veux un vrai singleton il faudrait aussi une classe figée sinon rien ne m'empêche de surcharger ce qui est mis en place et sealed n'est pas supporté sous D7 si mon souvenir est bon.

    après moi j'aime bien cette écriture du singleton qui fonction avec toutes les versions

    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
     
    unit MySingleton;
     
    interface
     
    var
      PublicValue: Integer;
     
    procedure MaMethod;
     
    function GetPrivateValue: Integer;
     
    implementation
     
    var
      PrivateValue: Integer;
     
    function GetPrivateValue: Integer;
    begin
      Result := PrivateValue;
    end;
     
    procedure MaMethod;
    begin
    end;
     
    end.
    et ça s'utilise comme ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    uses
      MySingleton;
     
    begin
      MySingleton.PublicValue := 1;
      MySingleton.GetPrivateValie;
      MySingleton.MaMethod();
    end;
    Bonjour Paul et merci pour ta contribution.
    D'habitude, lorsque je lis un de tes posts, soit je suis d'accord, soit j'apprends quelque chose (cas qui arrive le plus souvent et je t'en remercie. ).
    Mais dans ce cas précis, à moins d'avoir loupé quelque chose, j'ai du mal à voir le singleton :
    - Il n'y a pas de classe.
    - Si je remplace l'entier par une classe dans ton exemple, rien ne m'empêche de faire un Free sur PublicValue
    - Toujours en remplaçant l'entier par une classe, rien ne m'empêche non plus de créer plusieurs instance puisque la déclaration reste publique.

  20. #20
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    novembre 2002
    Messages
    6 755
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : novembre 2002
    Messages : 6 755
    Points : 21 577
    Points
    21 577

    Par défaut

    Citation Envoyé par popo Voir le message
    Bonjour Paul et merci pour ta contribution.
    D'habitude, lorsque je lis un de tes posts, soit je suis d'accord, soit j'apprends quelque chose (cas qui arrive le plus souvent et je t'en remercie. ).
    Mais dans ce cas précis, à moins d'avoir loupé quelque chose, j'ai du mal à voir le singleton :
    - Il n'y a pas de classe.
    - Si je remplace l'entier par une classe dans ton exemple, rien ne m'empêche de faire un Free sur PublicValue
    - Toujours en remplaçant l'entier par une classe, rien ne m'empêche non plus de créer plusieurs instance puisque la déclaration reste publique.
    haha mais c'est l'unité qui est un singleton à elle seule il est impossible de l'instancier deux fois dans le projet, il est même impossible de la désallouer, justement car ce n'est pas un objet

    bon ok, tu ne peux pas dériver d'une classe de base (encore que tu peux très bien appeler une unité CustomSingleton.pas qui déclarerait une function MaMethod)...mais pas de méthodes dynamique (sauf à ajouter des pointeurs de fonctions...mais bon...) et surtout pas de property.

    en fait ce que je voulais surtout souligner c'est que Delphi n'est pas un langage 100% objet, et je ne vois aucun mal à utiliser des variables et fonctions globales qui présentent le gros avantage d'avoir implicitement un espace de nom lié à leur unité : "NomUnit.NomFonction" qui dans l'IDE se comportant très exactement comme "Instance.Methode"

    Et pour les points 2 et 3 de ta remarque, c'est tout aussi vrai d'un singleton déclaré comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    type
      TSingleton = class()
      public
        MaVar: Integer;
      end;
    la différence est la possibilité d'utiliser une "property" alors qu'avec une approche non objet tu auras au mieux des getter et setter.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    interface // équivalent du public
     
    function GetVar: Integer;
    procedure SetVar(Value: Integer);
     
    implementation // équivalent du private
     
    var
      MaVar: Integer;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Produits : UPnP, RemoteOffice, FlashPascal

Discussions similaires

  1. Signaler plusieurs numéros par modem avec Delphi 7
    Par Ralliart dans le forum Débutant
    Réponses: 0
    Dernier message: 17/09/2007, 03h43
  2. Comment gérer des services par programmation avec Delphi ?
    Par isachat666 dans le forum API, COM et SDKs
    Réponses: 4
    Dernier message: 18/12/2005, 18h54

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