Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Notions fondamentales de Programmation Orientée Objet avec Free Pascal/Lazarus
Un tutoriel de Gilles Vasseur

Le , par Alcatîz

9PARTAGES

11  0 
POO à gogo !
Notions fondamentales de Programmation Orientée Objet avec Free Pascal/Lazarus

Faites-vous partie de ceux qui ne s'aventurent guère au-delà du noyau historique de Pascal ? Pourtant toutes les applications graphiques et tous les composants produits avec Lazarus sont tributaires de la Programmation Orientée Objet. Plus encore : bien qu'il ne se veuille pas contraignant, le compilateur Free Pascal qui sous-tend cet environnement a été entièrement pensé pour manipuler des objets à travers le concept de classe. Alors, lancez-vous ! Avec ce tutoriel, vous oserez enfin aborder les notions fondamentales relatives à la Programmation Orientée Objet avec Free Pascal.

http://gilles-vasseur.developpez.com...rientee-objet/

Et vous ?
Que pensez-vous de ce tutoriel ?
Avez-vous l'habitude de "penser POO" lorsque vous développez vos applications ?
Ou ce tutoriel vous incitera-t-il à le faire ?

Retrouver les meilleurs cours et tutoriels pour apprendre Lazarus
Retrouver les meilleurs cours et tutoriels pour apprendre la programmation

Une erreur dans cette actualité ? Signalez-le nous !

Avatar de gvasseur58
Responsable Lazarus & Pascal https://www.developpez.com
Le 21/06/2016 à 17:29
Bonjour,

Le tutoriel a été mis à jour :

  • correction de l'erreur signalée par Jipété ;
  • intégration des notes de bas de page ;
  • présentation améliorée (avec couleurs conservées) ;
  • reformulations et compléments de deux passages signalés comme équivoques ou incomplets ;
  • le lien vers les exemples est disponible (et correct !).


Des exemples supplémentaires sont en cours d'élaboration, car rien ne vaut la pratique !

Cordialement,

Gilles
7  0 
Avatar de gvasseur58
Responsable Lazarus & Pascal https://www.developpez.com
Le 20/06/2016 à 18:35
Salut Jipété !

Tout d'abord, merci pour ta lecture attentive dont je vais tirer le meilleur afin d'améliorer ce tutoriel. Je ne manquerai pas de te citer dans les personnes qui ont collaboré à ce texte.

Citation Envoyé par Jipété Voir le message

Page 4 au milieu, on découvre la déclaration de classe ; tout le monde en a déjà vu, mais il aurait été bon, dans un tuto partant de la base, d'expliquer les mots-clé qu'on y rencontre (private, public, etc.), surtout quand il y a des choses jamais vues par ailleurs, je pense à l'ajout du mot-clé "strict" : c'est quoi la différence entre "private" et "strict private" ? C'est encore plus private que private ?
Il est indiqué en note que la structure à ce niveau n'a pas à être maîtrisée : l'explication partielle vient un tout petit peu plus tard et la notion de portée (y compris strict private) est expliquée page 16, lorsqu'elle devient vraiment nécessaire. Je pouvais aussi montrer en premier lieu une classe mal ficelée et partielle, mais il m'a semblé plus honnête d'en montrer une "réelle" si l'on veut comparer. Plus privé que strict private, tu meurs !

Citation Envoyé par Jipété Voir le message

Typo page 5 : {$mode objfp} --> {$mode objfpc}
Je vais corriger...

Citation Envoyé par Jipété Voir le message

Page 7 : "préfixée du nom de l'objet qui la convoque, suivi d'un point" ? --> "préfixée du nom de l'objet qui l'invoque, suivie d'un point"
Ben non ! C'est bien l'objet qui est suivi d'un point et non la méthode. La construction est correcte (dixit l'éminent correcteur) : je vais cependant reformuler le tout pour le rendre plus clair.

Citation Envoyé par Jipété Voir le message

Il est curieux que la page intitulée "Notes Bas de Page" présente ces fameuses notes toutes regroupées en fin de document, ce qui ne facilite pas la lecture.
Je suis bien d'accord, mais je n'ai pas de prise sur ce phénomène. Je vais par conséquent essayer de supprimer toutes les notes pour les intégrer au texte. [En fait, il faut que j'apprenne à mieux me servir des outils internes :weird:]

Citation Envoyé par Jipété Voir le message

Et le truc le plus perturbant, pour moi, est l'absence d'espace, dans 99% des cas, entre le mot-clé en couleur et le mot qui suit. Un exemple parmi tant d'autres, à la 3e ligne de la page 7 :
Encore bien d'accord. Cette absence d'espace n'apparaît qu'à la mise en ligne ! Vais-je devoir supprimer la couleur ou créer des espaces superflus ? Un suspens intenable règne.

Citation Envoyé par Jipété Voir le message

Sinon, c'est bien, c'est très bien ! À quand la suite avec les machins virtuels, abstract, etc. ?
Bientôt

Cordialement,

Gilles

***************** Modification *****************
Le texte a été revu : la coquille a été corrigée ; les couleurs sont bien gérées (ajout d'un espace supplémentaire) ; les notes de bas de page ont été réduites au minimum. 20/06/2016 20h41.
3  0 
Avatar de gvasseur58
Responsable Lazarus & Pascal https://www.developpez.com
Le 20/06/2016 à 18:53
Bonjour ThWilliam !

Comme pour Jipété, merci pour ta lecture attentive et tes remarques qui vont m'aider à améliorer ce tutoriel.

Citation Envoyé par ThWilliam Voir le message
Notamment :

* l'accès aux propriétés par les "Getters" et "Setters". Dans TAnimal, le setter "SetNom" n'est pas utile, car la procédure ne fait qu'allouer la valeur à FNom. Un setter aurait été utile pour, p.ex., vérifier la valeur assignée en interdisant certaines choses.
Dans ce tutoriel, j'essaye de donner de bonnes habitudes : le setter n'est pas utile ici tant que les fondements de la classe restent ceux-ci, mais, rassure-toi, un tutoriel qui ne concerne que les propriétés est en cours de rédaction (en fait, il est terminé, mais doit être relu). Le sujet sera largement développé.

Citation Envoyé par ThWilliam Voir le message

* Le tuto parle très peu du constructeur, ce qui me parait être une notion fondamentale. Contrairement à ce qui est dit, j'utilise toujours un constructeur dans lequel j'initialise les différents champs même si ceux-ci gardent leur valeur par défaut. Habitude prise sous Delphi qui conseillait d'ailleurs vivement d'indiquer aussi la valeur par défaut dans la déclaration de propriété (quand c'est autorisé) :
property ASoif: Boolean read fASoif write fASoif default false;
N'oublions pas non plus que, dans beaucoup de cas, un champ de type integer (p.ex) doit être initialisé <> 0.
Ces remarques me plaisent bien. Si tu m'y autorises, je les intégrerais volontiers au texte. Tu seras bien entendu ajouté aux intervenants cités à la fin du tutoriel.

Pour tout le monde, trois tutoriels dans le même esprit sont en cours de finalisation :
1. les méthodes sous toutes leurs formes
2. les propriétés sous toutes leurs formes
3. les auxiliaires de classe

Bien sûr, si certains sont intéressés...

Cordialement,

Gilles
3  0 
Avatar de gvasseur58
Responsable Lazarus & Pascal https://www.developpez.com
Le 23/06/2016 à 11:13
Citation Envoyé par BeanzMaster Voir le message
Bonjour, merci pour tes explications complémentaires et ta réactivité Gilles, je comprend mieux l'utilité de strict qui est vraiment utile dans beaucoup de cas, faudra maintenant que je pense à mieux structurer mes classes . Juste une chose :

Code : Sélectionner tout
1
2
3
4
5
function TMyClass.InternalMyFunct: Integer;
begin
  Result := fMyStrictPrivateField;
  //Result := fMyStrictPrivateField; ne compile pas
end;
Pourquoi cet exemple ne fonctionne pas ? on est bien dans la même classe ? c'est parce que la fonction est "public" que l'on n'a pas acces a notre variable "strict private" ?

Merci
Bonjour,

Non : c'est juste que j'ai oublié d'effacer la seconde ligne qui est strictement la même que celle qui précède . J'ai inversé deux fonctions (comme quoi, la rapidité n'est pas toujours l'amie de l'exactitude ) :

Code : Sélectionner tout
1
2
3
4
5
function MyThirdClass.MyFunc: Integer;
begin
  Result := fMyPrivateField;
  //Result := fMyStrictPrivateField; ne compile pas
end;
Je corrige sur l'original de ce pas !

Cordialement.
3  0 
Avatar de Jipété
Expert éminent sénior https://www.developpez.com
Le 20/06/2016 à 11:24
Salut,

remarques plutôt sur la forme que sur le fond, mais bon, je suis comme ça, si la route est mal fichue le trajet va m'être pénible

Page 4 au milieu, on découvre la déclaration de classe ; tout le monde en a déjà vu, mais il aurait été bon, dans un tuto partant de la base, d'expliquer les mots-clé qu'on y rencontre (private, public, etc.), surtout quand il y a des choses jamais vues par ailleurs, je pense à l'ajout du mot-clé "strict" : c'est quoi la différence entre "private" et "strict private" ? C'est encore plus private que private ?

Il y a bien une explication succincte en page 11 (en page 11 ! Rappel : je suis en train de lire la page 4...), mais qui n'apporte rien de plus que ce qu'on sait déjà (quand on le sait).
Ah, la vraie explication se trouve en page 12 : une indication en page 4 serait la bienvenue, àmha.

Typo page 5 : {$mode objfp} --> {$mode objfpc}

Page 7 : "préfixée du nom de l'objet qui la convoque, suivi d'un point" ? --> "préfixée du nom de l'objet qui l'invoque, suivie d'un point"

Il est curieux que la page intitulée "Notes Bas de Page" présente ces fameuses notes toutes regroupées en fin de document, ce qui ne facilite pas la lecture, ni la navigation dans le document : en cliquant sur le n° de la note, on se retrouve à la fin du document mais on n'est pas obligé de se souvenir du fameux n° donc quelle note dois-je lire ? Et en fin de lecture, penser à re-cliquer sur le n° pour retourner où l'on était... Les notes en bas de page sont quand même plus confortables : on ne perd pas son fil.

Et le truc le plus perturbant, pour moi, est l'absence d'espace, dans 99% des cas, entre le mot-clé en couleur et le mot qui suit. Un exemple parmi tant d'autres, à la 3e ligne de la page 7 :


Sinon, c'est bien, c'est très bien ! À quand la suite avec les machins virtuels, abstract, etc. ?
2  0 
Avatar de ThWilliam
Membre chevronné https://www.developpez.com
Le 20/06/2016 à 13:12
Bonjour !

Bravo à Gilles pour son tuto.

J'aurais souhaité cependant que certaines notions soient plus explicites pour un débutant en POO.

Notamment :

* l'accès aux propriétés par les "Getters" et "Setters". Dans TAnimal, le setter "SetNom" n'est pas utile, car la procédure ne fait qu'allouer la valeur à FNom. Un setter aurait été utile pour, p.ex., vérifier la valeur assignée en interdisant certaines choses.

* Le tuto parle très peu du constructeur, ce qui me parait être une notion fondamentale. Contrairement à ce qui est dit, j'utilise toujours un constructeur dans lequel j'initialise les différents champs même si ceux-ci gardent leur valeur par défaut. Habitude prise sous Delphi qui conseillait d'ailleurs vivement d'indiquer aussi la valeur par défaut dans la déclaration de propriété (quand c'est autorisé) :
property ASoif: Boolean read fASoif write fASoif default false;
N'oublions pas non plus que, dans beaucoup de cas, un champ de type integer (p.ex) doit être initialisé <> 0.

Cordialement
Thierry
2  0 
Avatar de BeanzMaster
Membre expert https://www.developpez.com
Le 21/06/2016 à 12:03
Bonjour Gilles, article intéressant, juste un petit truc dans la section II-B. Notion de portée

strict private: l'élément n'est visible que par un élément de la même classe ; --> sauf par transtypage non ?
private: l'élément n'est visible que par un élément présent dans la même unité;
--> idem que ci-dessus
--> et qu'entend tu par élément ???? une procédure de la classe ? ( une fonction hors de la classe, mais dans la même unité n'aura pas accès a ces données, non ? )
--> "un élément présent dans la même unité" ça ne serait pas plutôt par un élément de la même classe comme "strict"

strict protected: l'élément n'est utilisable que par un descendant de la classe (donc une classe dérivée) présent dans l'unité ou dans une autre unité que celle de la classe ;
protected: l'élément n'est utilisable que par un descendant de la classe, qu'il soit dans l'unité de la classe ou dans une autre unité y faisant référence, ou par une autre classe présente dans l'unité de la classe ;

--> les 2 c'est pareil, non ?

Il serait le bienvenue de faire un exemple de classe comportant une section "strict private" et "private" pour bien comprendre la différence. La je ne voit pas ce qu"apporte de plus l'utilisation de "strict" et qu'elle est l'avantage de l'utiliser a part faire "jolie"

Bien à toi et bonne continuation dans tes tutos
Jérôme
2  0 
Avatar de BeanzMaster
Membre expert https://www.developpez.com
Le 23/06/2016 à 10:13
Bonjour, merci pour tes explications complémentaires et ta réactivité Gilles, je comprend mieux l'utilité de strict qui est vraiment utile dans beaucoup de cas, faudra maintenant que je pense à mieux structurer mes classes . Juste une chose :

Code : Sélectionner tout
1
2
3
4
5
function TMyClass.InternalMyFunct: Integer;
begin
  Result := fMyStrictPrivateField;
  //Result := fMyStrictPrivateField; ne compile pas
end;
Pourquoi cet exemple ne fonctionne pas ? on est bien dans la même classe ? c'est parce que la fonction est "public" que l'on n'a pas acces a notre variable "strict private" ?

Merci
2  0 
Avatar de ThWilliam
Membre chevronné https://www.developpez.com
Le 20/06/2016 à 20:56
Bonsoir Gilles,

"Ces remarques me plaisent bien. Si tu m'y autorises, je les intégrerais volontiers au texte."
Of course ! La question ne se pose même pas.
Et ne te sens pas obligé de me citer parmi les intervenants.

Thierry
1  0 
Avatar de gvasseur58
Responsable Lazarus & Pascal https://www.developpez.com
Le 21/06/2016 à 16:42
Bonjour,

Merci à toi aussi pour cette lecture attentive
Citation Envoyé par BeanzMaster Voir le message

strict private: l'élément n'est visible que par un élément de la même classe ; --> sauf par transtypage non ?
private: l'élément n'est visible que par un élément présent dans la même unité;
--> idem que ci-dessus
--> et qu'entend tu par élément ???? une procédure de la classe ? ( une fonction hors de la classe, mais dans la même unité n'aura pas accès a ces données, non ? )
--> "un élément présent dans la même unité" ça ne serait pas plutôt par un élément de la même classe comme "strict"
Non, pas de transtypage qui tienne ! En revanche, une fonction hors de la classe, mais dans la même unité, aura accès à ces données y compris privées (si elle accède à une instance d'une classe, bien sûr). Donc, je maintiens les définitions.

Je vais en revanche apporter un exemple pour preuve et te remercie pour cette remarque .

Voici tout d'abord l'unité d'un projet qui définit mes classes, ainsi qu'une procédure indépendante :

Code : Sélectionner tout
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
unit other;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;

type

  { TMyClass }

  TMyClass = class
    strict private
      fMyStrictPrivateField: Integer;
    private
      fMyPrivateField: Integer;
    public
      fMyPublicField: Integer;
      function InternalMyFunct: Integer;
  end;

  { TMyOtherClass }

  TMyOtherClass = class
    public
      ASubClass: TMyClass;
      constructor Create;
      destructor Destroy; override;
  end;

  { MyThirdClass }

  MyThirdClass = class(TMyClass)
    public
      function MyFunc: Integer;
    end;

  procedure MyProc;

implementation

procedure MyProc;
var
  AnObject: TMyClass;
begin
  AnObject := TMyClass.Create;
  try
    AnObject.fMyPublicField := 1;
    AnObject.fMyPrivateField := 3;
    //AnObject.fMyStrictPrivateField := 2; ne compile pas
  finally
    AnObject.Free;
  end;
end;

{ MyThirdClass }

function MyThirdClass.MyFunc: Integer;
begin
  Result := fMyPrivateField;
  //Result := fMyStrictPrivateField; ne compile pas
end;

{ TMyClass }

function TMyClass.InternalMyFunct: Integer;
begin
  Result := fMyStrictPrivateField;
end;

{ TMyOtherClass }


constructor TMyOtherClass.Create;
begin
  inherited;
  ASubClass := TMyClass.Create;
  ASubClass.fMyPrivateField := 1;
  ASubClass.fMyPrivateField := 4;
  // ASubClass.fMyStrictPrivateField:= 5; ne compile pas
end;

destructor TMyOtherClass.Destroy;
begin
  ASubClass.Free;
  inherited Destroy;
end;

end.
On voit que les champs privés ne le sont pas vraiment puisqu'ils sont même accessibles par une procédure indépendante qui instancie une classe définie dans l'unité. D'où l'intérêt de strict private qui interdit tout accès, y compris à une classe qui descend de la première.

A présent, si l'on crée un autre unité faisant référence à celle qui définit les classes, les choses changent, car ce qui est privé n'est plus accessible :
Code : Sélectionner tout
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
unit other2;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, other;

type

  { TMyFourthClass }

  TMyFourthClass = class(TMyClass)
    public
      procedure MyProc;
  end;

procedure MyProc;

implementation


procedure MyProc;
var
  AnObject: TMyClass;
begin
  AnObject := TMyClass.Create;
  try
    AnObject.fMyPublicField := 1;
    // AnObject.fMyPrivateField := 3; ne compile pas
    //AnObject.fMyStrictPrivateField := 2; ne compile pas
  finally
    AnObject.Free;
  end;
end;

{ TMyFourthClass }

procedure TMyFourthClass.MyProc;
begin
  fMyPublicField:= 7;
  //fMyPrivateField := 9; ne compile pas
  //fMyStrictPrivateField := 10; ne compile pas
end;


end.
J'ai choisi une procédure indépendante et une classe fille. Dans les deux cas, ce qui était accessible dans la même unité ne l'est plus hors de l'unité : le privé est enfin privé !

Citation Envoyé par BeanzMaster Voir le message
strict protected: l'élément n'est utilisable que par un descendant de la classe (donc une classe dérivée) présent dans l'unité ou dans une autre unité que celle de la classe ;
protected: l'élément n'est utilisable que par un descendant de la classe, qu'il soit dans l'unité de la classe ou dans une autre unité y faisant référence, ou par une autre classe présente dans l'unité de la classe ;
--> les 2 c'est pareil, non ?
Pas tout à fait : comme indiqué, avec protected, une autre classe (donc quelconque) qui figure dans l'unité de définition de la classe où apparaît ce protected a accès aux méthodes et aux champs protégés. Avec strict protected, il faut être descendant de la classe pour y avoir accès : une classe non dérivée, même si elle figure dans la même unité que la classe qui comprend le strict protected, n'aura pas accès à ces éléments (champs ou méthodes). Là aussi, un exemple détaillé s'impose sans doute...

Remarque complémentaire : les détails donnés ici ne concernent pas vraiment les débutants en POO qui utiliseront assez facilement les "classiques" private, protected, public et published et s'abstiendront sans doute d'utiliser les dérivés strict. Ces derniers ne sont pas pour faire "joli" et intéressent tout spécialement les projets assez ambitieux. Je pense par ailleurs que strict private devrait devenir un réflexe tant cette section est utile afin d'éviter les manipulations risquées de champs.

J'ai conscience de ne pas pouvoir répondre, même en 26 pages, à toutes les questions, mais je promets de proposer des exercices/exemples complémentaires qui illustreront ces problèmes, sans doute dans un tutoriel du genre "Exercices sur la POO". Encore merci pour l'intérêt porté à ce tutoriel perfectible, c'est certain .

Cordialement,

Gilles
1  0