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

Free Pascal Discussion :

Motif Etat : libération objet [Free Pascal]


Sujet :

Free Pascal

  1. #1
    Membre éclairé

    Profil pro
    Inscrit en
    Mai 2002
    Messages
    641
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 641
    Par défaut Motif Etat : libération objet
    Bonjour,

    J'implémente le motif Etat en Free Pascal.

    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
    TState = class
    ...
    end;
     
    TContext = class
        State : TState;
        procedure SetState(AState: TState);
    end;
     
    TDefaultState = class(TState)
    ....
     
    procedure TContext.SetState(AState: TState);
    begin
        Self.State.Free;
        Self.State := AState;
    end;
     
    procedure TDefaultState.Run;
    begin
        if ... then
            Self.Context.SetState(TNewState.Create(Self.Context));
    end;
    Je me pose une question concernant la libération de l'objet. Lorsque je change d'état, je libère donc l'ancien objet état en appelant TContext.SetState... Est-ce la bonne façon de faire ?

  2. #2
    Membre Expert
    Avatar de Dr.Who
    Inscrit en
    Septembre 2009
    Messages
    980
    Détails du profil
    Informations personnelles :
    Âge : 46

    Informations forums :
    Inscription : Septembre 2009
    Messages : 980
    Par défaut
    J'aurais plutôt eu cette approche, en ajoutant des sécurités :

    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
    type
      TContext = class;
     
      TState = class
      private
        fContext: TContext;
      protected
        function Value:integer; virtual; abstract;
      public
        constructor Create(aContext: TContext); virtual;
        property Context: TContext read fContext;
      end;
     
      TStateClass = class of TState;
     
      TContext = class
      private
        fState : TState;
      public
        property State : TState read fState;
        procedure setState(Value: TStateClass);
        constructor Create; overload; virtual;
        constructor Create(aState: TStateClass); overload; virtual;
        destructor Destroy; override;
      end;
     
      TStateA = class(TState)
      public
        function Value: integer; override;
      end;
     
      TStateB = class(TState)
      public
        function Value: integer; override;
      end;
     
      TStateC = class(TStateB)
      public
        function Value: integer; override;
      end;
     
    { TState }
     
    constructor TState.Create(aContext: TContext);
    begin
      inherited Create;
     
      fContext := aContext;
    end;
     
    { TContext }
     
    constructor TContext.Create;
    begin
      inherited;
     
      fState := nil;
    end;
     
    constructor TContext.Create(aState: TStateClass);
    begin
      inherited Create;
     
      setState(aState);
    end;
     
    destructor TContext.Destroy;
    begin
      if assigned(fState) then
      begin
        FreeAndNil(fState);
      end;
     
      inherited;
    end;
     
    procedure TContext.setState(Value: TStateClass);
    begin
      if Assigned(fState) then
      begin
        FreeAndNil(fState);
      end;
     
      fState := Value.Create(self);
    end;
     
    { TStateA }
     
    function TStateA.Value: integer;
    begin
      result := 1;
    end;
     
    { TStateB }
     
    function TStateB.Value: integer;
    begin
      result := 2;
    end;
     
    { TStateC }
     
    function TStateC.Value: integer;
    begin
      result := inherited Value + 1;
    end;

    et on contrôle que ça fonctionne bien :

    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
    var
      CTX : TContext;
     
    begin
      // -- //
      CTX := TContext.Create;
     
        CTX.setState(TStateA);
        writeln('State = ', CTX.State.Value);
     
        CTX.setState(TStateB);
        writeln('State = ', CTX.State.Value);
     
      CTX.Free;
     
      // -- //
     
      with TContext.Create(TStateC) do
      try
        writeln('State = ', State.value);
      finally
        Free;
      end;
     
      // -- //
     
      readLn;
    end.

    ce qui nous affiche :
    Code autre : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    State = 1
    State = 2
    State = 3


    Bien sur, la on reste dans le Type class. Logiquement on devrait travailler avec des Interface et se retrouver donc avec au moins IState, IContext, IStateController...
    [ Sources et programmes de Dr.Who | FAQ Delphi | FAQ Pascal | Règlement | Contactez l'équipe ]
    Ma messagerie n'est pas la succursale du forum... merci!

  3. #3
    Membre éclairé

    Profil pro
    Inscrit en
    Mai 2002
    Messages
    641
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 641
    Par défaut
    Bonjour Dr.Who,

    C'est effectivement plus rigoureux. Je ne connaissais pas le type "class of"...

    Par contre avec cette approche je ne peux pas gérer la transition depuis l'objet état, ce qui me gène.

  4. #4
    Membre Expert
    Avatar de Dr.Who
    Inscrit en
    Septembre 2009
    Messages
    980
    Détails du profil
    Informations personnelles :
    Âge : 46

    Informations forums :
    Inscription : Septembre 2009
    Messages : 980
    Par défaut
    il suffit de modifier comme ceci, en effet je n'avait pas prévus ce cas :


    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
    {
    get/set State :
      TState.SetState(context cible): nouvelle instance TState du Context
        * setState appel TState.Create(context cible)
        * assertion fail si context est invalide
     
      TContext.GetState(class of TState): nouvelle instance TState du Context
        * getState appel TState.setState(context cible)
     
      * get/set detruit automatiquement l'instance State en cours dans le context
        et le remplace par une nouvelle instance.
     
    }
    type
      TContext = class;
     
      TState = class
      private
        fContext: TContext;
      protected
        function Value:integer; virtual; abstract;
      public
        class function setState(aContext: TContext): TState; virtual;
        constructor Create(aContext: TContext); virtual;
        property Context: TContext read fContext;
      end;
     
      TStateClass = class of TState;
     
      TContext = class
      private
        fState : TState;
      public
        property State : TState read fState;
        function getState(aStateClass: TStateClass): TState; virtual;
        constructor Create; overload; virtual;
        constructor Create(aState: TStateClass); overload; virtual;
        destructor Destroy; override;
      end;
     
      TStateA = class(TState)
      public
        function Value: integer; override;
      end;
     
      TStateB = class(TState)
      public
        function Value: integer; override;
      end;
     
      TStateC = class(TStateB)
      public
        function Value: integer; override;
      end;
     
    { TState }
     
    constructor TState.Create(aContext: TContext);
    begin
      inherited Create;
     
      assert(assigned(aContext), 'Context non définit.');
     
      fContext := aContext;
     
      if assigned(fContext.fState) then
        freeAndNil(aContext.fState);
     
      fContext.fState := self;
    end;
     
     
    class function TState.setState(aContext: TContext): TState;
    begin
      result := Create(aContext);
    end;
     
    { TContext }
     
    constructor TContext.Create;
    begin
      inherited;
     
      fState := nil;
    end;
     
    constructor TContext.Create(aState: TStateClass);
    begin
      inherited Create;
     
      getState(aState);
    end;
     
    destructor TContext.Destroy;
    begin
      if assigned(fState) then
        FreeAndNil(fState);
     
      inherited;
    end;
     
    function TContext.getState(aStateClass: TStateClass): TState;
    begin
      result := aStateClass.setState(Self);
    end;
     
    { TStateA }
     
    function TStateA.Value: integer;
    begin
      result := 1;
    end;
     
    { TStateB }
     
    function TStateB.Value: integer;
    begin
      result := 2;
    end;
     
    { TStateC }
     
    function TStateC.Value: integer;
    begin
      result := inherited Value + 1;
    end;

    Et l'utilisation :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        CTX := TContext.Create;
     
          writeln('State get A = ', CTX.getState(TStateA).Value );
          writeln('State get B = ', CTX.getState(TStateB).Value );
          writeln('State get C = ', CTX.getState(TStateC).Value );
     
          writeln('State set A = ', TStateA.setState(CTX).Value );
          writeln('State set B = ', TStateB.setState(CTX).Value );
          writeln('State set C = ', TStateC.setState(CTX).Value );
     
        CTX.Free;

    Tu remarquera la présence de "class function" pour setState.

    Cette écriture permet de définir une méthode fonctionnelle avec ou sans instance valide de la classe.
    on peux donc appeler setState plutôt que Create, d'ailleur SetState appelle Create. Comme dans l'exemple.

    Et l'on peut donc faire ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TStateA.setState(CTX1).setState(CTX2).setState(CTX3);
    Et la tu vas me dire Whazefukizviz ?

    et bien on viens tout simplement de créer en une seule ligne, 3 instance distinctes de TStateA dans CTX1, CTX2 et CTX3.

    Plus tard dans le programme, nous ne savons pas quel état aura CTX3 par exemple, mais nous voulons synchroniser les états avec CTX2 et CTX1 on fera donc ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    CTX3.State.SetState(CTX1).SetState(CTX2);
    Et voila, CTX1 et CTX2 ont maintenant la même classe état que CTX3.

    Pour savoir qu'elle est précisément la classe état, il suffit d'intérroger le ClassName ou ClassType de Context.State

    exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if CTX1.State.ClassName = 'TStateA' then

    Ah, une dernière chose avec la fonction setState :


    Il vas de soit que la propriété State, même initialisée à nil (donc pas encore définie) peut appeler setState !

    par contre, si l'on l'appel directement on créera une instance abstraite de TState (puisque TState est la class abstraite de tout TState ), il faudra donc transtyper comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    CTX.Create; // <- State est nil pour l'instant
     
    TStateA(CTX.State).SetState(CTX); // <- héhéhé
     
    (CTX.State as TStateB).setState(CTX); // <- huhuhu

    Exemple fonctionnel un peu plus poussé :

    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
     
    var
      CTX1,CTX2,CTX3 : TContext;
      N : integer;
     
    const
      States : array[0..2] of TStateClass = (TStateA, TStateB, TStateC);
     
    begin
     
      Randomize;
     
      try
        CTX1 := TContext.Create;
        CTX2 := TContext.Create;
        CTX3 := TContext.Create;
        try
          writeln('State get A = ', CTX1.getState(TStateA).Value );
          writeln('State get B = ', CTX1.getState(TStateB).Value );
          writeln('State get C = ', CTX1.getState(TStateC).Value );
          writeln('');
     
          writeln('State set A = ', TStateA.setState(CTX1).Value );
          writeln('State set B = ', TStateB.setState(CTX1).Value );
          writeln('State set C = ', TStateC.setState(CTX1).Value );
          writeln('');
     
          // un état alléatoire, on ne sais pas lequel
          States[random(100) mod 3].setState(CTX1);
     
          // synchro CTX2 et CTX3
          CTX1.State.setState(CTX2).setState(CTX3);
          writeln('State of CTX1 is ', CTX1.State.ClassName, ' = ', CTX1.State.Value );
          writeln('State of CTX2 is ', CTX2.State.ClassName, ' = ', CTX2.State.Value );
          writeln('State of CTX3 is ', CTX3.State.ClassName, ' = ', CTX3.State.Value );
          writeln('');
     
          // stress test
          for N := 0 to 2 do
          begin
            // un autre état alléatoire, on ne sais pas lequel mais doit être différent
            // de l'ancien contenus dans CTX2
            repeat
              States[random(100) mod 3].setState(CTX1);
            until CTX1.State.ClassType <> CTX2.State.ClassType;
     
     
            // synchro CTX2 et CTX3
            CTX1.State.setState(CTX2).setState(CTX3);
            writeln('State of CTX1 is ', CTX1.State.ClassName, ' = ', CTX1.State.Value );
            writeln('State of CTX2 is ', CTX2.State.ClassName, ' = ', CTX2.State.Value );
            writeln('State of CTX3 is ', CTX3.State.ClassName, ' = ', CTX3.State.Value );
            writeln('');
          end;
     
        finally
          CTX3.Free;
          CTX2.Free;
          CTX1.Free;
        end;
      except
        on E:Exception do
          Writeln(E.Classname, ': ', E.Message);
      end;
     
      readLn;
    end.
    [ Sources et programmes de Dr.Who | FAQ Delphi | FAQ Pascal | Règlement | Contactez l'équipe ]
    Ma messagerie n'est pas la succursale du forum... merci!

  5. #5
    Membre Expert
    Avatar de Dr.Who
    Inscrit en
    Septembre 2009
    Messages
    980
    Détails du profil
    Informations personnelles :
    Âge : 46

    Informations forums :
    Inscription : Septembre 2009
    Messages : 980
    Par défaut
    Salut, ou en est tu de ce sujet ? tu as pus avancer ?
    [ Sources et programmes de Dr.Who | FAQ Delphi | FAQ Pascal | Règlement | Contactez l'équipe ]
    Ma messagerie n'est pas la succursale du forum... merci!

  6. #6
    Membre éclairé

    Profil pro
    Inscrit en
    Mai 2002
    Messages
    641
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 641
    Par défaut
    Bonjour,

    Désolé de ne pas avoir répondu plus tôt. J'ai été assez occupé ces derniers jours...

    Mais je confirme que ton approche répond bien à mon problème. Merci.

  7. #7
    Membre Expert
    Avatar de Dr.Who
    Inscrit en
    Septembre 2009
    Messages
    980
    Détails du profil
    Informations personnelles :
    Âge : 46

    Informations forums :
    Inscription : Septembre 2009
    Messages : 980
    Par défaut
    Cool !
    [ Sources et programmes de Dr.Who | FAQ Delphi | FAQ Pascal | Règlement | Contactez l'équipe ]
    Ma messagerie n'est pas la succursale du forum... merci!

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

Discussions similaires

  1. Libération objet complexe
    Par Guybrush dans le forum Langage
    Réponses: 7
    Dernier message: 24/05/2011, 13h45
  2. Garbage Collector/libération objets référencés
    Par LeSmurf dans le forum Général Java
    Réponses: 3
    Dernier message: 17/12/2006, 19h47
  3. Réponses: 13
    Dernier message: 03/04/2006, 10h01
  4. Question simple sur la libération des objets
    Par gibet_b dans le forum Langage
    Réponses: 2
    Dernier message: 12/07/2004, 10h01
  5. [MFC] libération des objets GDI's
    Par Kevgeii dans le forum MFC
    Réponses: 5
    Dernier message: 01/02/2004, 10h37

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