Notions fondamentales de Programmation Orientée Objet avec Free Pascal/Lazarus
Un tutoriel de Gilles Vasseur
Le 2016-06-19 21:15:47, par Alcatîz, Responsable Pascal, Lazarus et Assembleur
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
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.
Et vous ?
-
gvasseur58Responsable Lazarus & PascalBonjour,
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,
Gillesle 21/06/2016 à 17:29 -
gvasseur58Responsable Lazarus & PascalSalut 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.
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 !
Je vais corriger...
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.
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:]
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.
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.le 20/06/2016 à 18:35 -
gvasseur58Responsable Lazarus & PascalBonjour ThWilliam !
Comme pour Jipété, merci pour ta lecture attentive et tes remarques qui vont m'aider à améliorer ce tutoriel.
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é.
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,
Gillesle 20/06/2016 à 18:53 -
gvasseur58Responsable Lazarus & PascalBonjour,
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 : 1
2
3
4
5function MyThirdClass.MyFunc: Integer; begin Result := fMyPrivateField; //Result := fMyStrictPrivateField; ne compile pas end;
Cordialement.le 23/06/2016 à 11:13 -
JipétéExpert éminent séniorSalut,
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. ?le 20/06/2016 à 11:24 -
ThWilliamMembre chevronné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
Thierryle 20/06/2016 à 13:12 -
BeanzMasterExpert confirmé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ômele 21/06/2016 à 12:03 -
BeanzMasterExpert confirmé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 : 1
2
3
4
5function TMyClass.InternalMyFunct: Integer; begin Result := fMyStrictPrivateField; //Result := fMyStrictPrivateField; ne compile pas end;
Mercile 23/06/2016 à 10:13 -
ThWilliamMembre chevronnéBonsoir Gilles,"Ces remarques me plaisent bien. Si tu m'y autorises, je les intégrerais volontiers au texte."
Et ne te sens pas obligé de me citer parmi les intervenants.
Thierryle 20/06/2016 à 20:56 -
gvasseur58Responsable Lazarus & PascalBonjour,
Merci à toi aussi pour cette lecture attentive
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 : 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
91unit 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.
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 : 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
49unit 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.
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,
Gillesle 21/06/2016 à 16:42