Dans notre exemple la méthode copier de la sous-classe R´epertoire doit être implantée différemment de la méthode codée au niveau de la classe Fichier, car, outre la copie du fichier-rép
Trang 1qui raffinent la description des objets de la super-classe est un aspect inséparable de
la spécialisation
Surcharge
Enfin la surcharge est le mécanisme qui consiste à enrichir, voire à remplacer
com-plètement, un comportement défini au niveau de la super-classe par un autre, adapté aux caractéristiques de la classe spécialisée
REMARQUE –Attention, la documentation PHP utilise le terme « surcharge » (overloading)
dans un sens différent de celui consacré en programmation objet La notion de surcharge présentée ici est conforme avec celle classique, rencontrée en C++ ou en Java.
Dans notre exemple la méthode copier() de la sous-classe R´epertoire doit être implantée différemment de la méthode codée au niveau de la classe Fichier, car, outre la copie du fichier-répertoire lui-même, on doit également copier
l’en-semble des fichiers contenus dans le répertoire Ce comportement de la méthode
copier() est tout à fait spécifique à ce type de fichier et nécessite toute la « sur-charge » – la redéfinition – de la méthode héritée Voici, très simplifié, l’essentiel des instructions que l’on pourrait trouver dans cette surcharge :
c l a s s R é p e r t o i r e {
/ / P r o p r i é t é s
p r i v a t e $ l i s t e _ f i c h i e r s ;
/ / Une m é t h o d e
p u b l i c c o p i e r ( $ d e s t i n a t i o n )
{
/ / On commence p a r c o p i e r l e r é p e r t o i r e l u i −même
/ / e n a p p e l a n t l a m é t h o d e d e l a s u p e r −c l a s s e
p a r e n t : : c o p i e r ( $ d e s t i n a t i o n ) ;
/ / P u i s on c o p i e t o u s l e s f i c h i e r s c o n t e n u s
f o r e a c h ( $ t h i s−> l i s t e _ f i c h i e r a s $ f i c h i e r )
$ f i c h i e r−>c o p i e r ( $ d e s t i n a t i o n ) ;
}
Cette méthode se décompose clairement en deux parties : l’une consistant à effectuer une copie standard, telle qu’elle est définie au niveau de la classe parent, l’autre répercutant la demande de copie sur l’ensemble des fichiers contenus dans le répertoire et référencés par la propriété ajoutéee liste_fichiers
Pour appliquer la copie standard, on doit appeler le code défini au niveau de la classe parente On utilise pour cela la construction parent::copier Cette pratique
est d’usage dans tous les cas, fréquents, ó la surcharge consiste à enrichir le
compor-tement défini au niveau de la classe générique, ce qui implique de conserver ce com-portement tout en lui ajoutant de nouvelles instructions Ces nouvelles instructions consistent ici à parcourir l’ensemble des fichiers contenus en leur appliquant à leur
tour la méthode copier().
Trang 2f o r e a c h ( $ t h i s−> l i s t e _ f i c h i e r s a s $ f i c h i e r )
$ f i c h i e r−>c o p i e r ( $ d e s t i n a t i o n ) ;
À chaque étape de la boucle, la variable $fichier référence un des fichiers contenus dans le répertoire, et on demande à cet objet de se copier vers la destination
Il s’agit d’un excellent exemple du processus d’abstraction consistant à voir selon les circonstances ces fichiers comme des objets « génériques » (instance de la classe Fichier) ou comme des objets spécialisées, instances des sous-classes de Fichier
Il faut imaginer ici, pour se limiter à notre exemple, que les fichiers contenus dans
un répertoire peuvent être soit des fichiers texte, soit eux-mêmes des répertoires contenant d’autres fichiers En les considérant uniformément, dans la boucle, comme des instances de Fichier dotés d’une méthode de copie, on s’évite le souci d’avoir
à distinguer les différents types d’actions à effectuer en fonction du type précis de fichier manipulé, et on laisse à l’objet lui-même le soin de déterminer la méthode à appliquer
Ce type de programmation peut sembler subtil quand on y est confronté les premières fois, mais il s’acquiert d’autant plus vite qu’on est convaincu du gain apporté par un raisonnement en termes génériques sans avoir à se soucier à chaque instant des détails d’implantation La simplicité du code obtenu une fois qu’on a résolu le problème de la conception et de la modélisation d’une application objet vient largement compenser l’effort initial à fournir
3.1.5 Spécialisation et classes abstraites : la classe BD
Voyons un exemple complet qui nous permettra également d’introduire un dernier concept La suite du chapitre consistera à approfondir, par la conception et l’implan-tation de plusieurs classes, tout ce qui est résumé ici
Notre exemple consiste ici à définir, en recourant à la spécialisation objet, un ensemble de classes définissant de manière uniforme les accès à une base de données relationnelle, quelle qu’elle soit Nous allons prendre comme cibles MySQL, Post-greSQL et ORACLE, avec comme objectif la possibilité de définir des applications qui utilisent indifféremment l’un ou l’autre système, et ce de manière totalement transparente Le site décrit dans la seconde partie de l’ouvrage s’appuie sur ces classes pour rendre le code compatible avec tout système relationnel
REMARQUE –Le code proposé ici fonctionne correctement, mais il est surtout conçu comme une illustration des concepts orientés-objet Comme signalé précédemment, l’interface PDO
de PHP offre une solution normalisée et plus complète Reportez-vous page 238 pour une introduction à PDO.
En termes de spécialisation, il n’y a aucune raison de dire qu’une classe définissant
les interactions avec PostgreSQL hérite de celle accédant à MySQL, ou l’inverse La
bonne question à se poser est toujours « un objet instance de la classe spécialisée est-il aussi un objet de la classe générique ? » La réponse est clairement non puisqu’un objet accédant à MySQL n’est pas un objet accédant à PostgreSQL et vice-versa En revanche tous deux sont des exemples d’un concept commun, celui d’objet accédant
Trang 3à une base de données Ce concept commun n’a pas d’instanciation : il n’existe pas d’objet qui fournisse ce comportement indépendamment d’un choix concret d’une base de données spécifique
Quand on a besoin de définir un comportement commun à un ensemble d’objets sans que ce comportement puisse être directement instancié, on utilise la notion
de classe abstraite qui permet de factoriser la description des méthodes fournies par
tous les objets instances des classes spécialisées, à charge pour ces classes de définir l’implantation appropriée de chacune de ces méthodes Dans notre cas, il s’agit de définir toutes les méthodes (au sens précis de : noms, liste des paramètres en entrée
et en sortie, rôle de la méthode) communes à tous les objets, quel que soit le SGBD auquel ils permettent d’accéder Il nous faut au minimum :
1 une méthode de connexion ;
2 une méthode d’exécution des requêtes ;
3 une ou plusieurs méthodes pour récupérer le résultat ;
4 une méthode pour traiter les apostrophes ou autres caractères gênants dans les chaînes à insérer dans les requêtes (la technique d’échappement pouvant varier d’un SGBD à un autre) ;
5 une gestion des erreurs
Au moment de la définition de ces méthodes, il faut s’assurer qu’elles
corres-pondent à des fonctionnalités qui peuvent être fournies par tous les SGBD Il faut
également réfléchir soigneusement aux paramètres d’entrée et de sortie nécessaires
à chaque méthode (on désigne souvent par signature cette spécification des
para-mètres) En d’autres termes, on doit définir de manière générique, c’est-à-dire sans
se soucier des détails d’implantation, l’interface d’accès à un SGBD en évitant de se laisser influencer, à ce stade, par les particularités de l’un d’entre eux Voici la classe abstraite BD
Exemple 3.3 exemples/BD.php:La classe abstraite BD
<? php
/ / C l a s s e a b s t r a i t e d é f i n i s s a n t u n e i n t e r f a c e g é n é r i q u e d ’ a c c è s / / à u n e b a s e d e d o n n é e s V e r s i o n s i m p l i f i é e : u n e d é f i n i t i o n
/ / p l u s c o m p l è t e e s t d o n n é e a v e c l e s i t e F i l m s
a b s t r a c t c l a s s BD
{
/ / −−−− P a r t i e p r i v é e : l e s p r o p r i é t é s
p r o t e c t e d $ c o n n e x i o n , $nom_base ;
/ / C o n s t r u c t e u r d e l a c l a s s e
f u n c t i o n _ _ c o n s t r u c t ( $ l o g i n , $ m o t _ d e _ p a s s e , $ b a s e , $ s e r v e u r ) {
/ / On c o n s e r v e l e nom d e l a b a s e
$ t h i s−>nom_base = $ba s e ;
Trang 4/ / C o n n e x i o n au s e r v e u r p a r a p p e l à u n e m é t h o d e p r i v é e
$ t h i s−>connexion = $ t h i s −>connect ( $log in , $mot_de_passe ,
$ b a s e , $ s e r v e u r ) ;
/ / L a n c é d ’ e x c e p t i o n e n c a s d ’ e r r e u r
i f ( $ t h i s−>connexion == 0)
t h r o w new E x c e p t i o n ( " E r r e u r de c o n n e x i o n au SGBD" ) ;
/ / F i n du c o n s t r u c t e u r
}
/ / M é t h o d e s p r i v é e s
a b s t r a c t p r o t e c t e d f u n c t i o n c o n n e c t ( $ l o g i n ,
$ m o t _ d e _ p a s s e , $ b a s e , $ s e r v e u r ) ;
a b s t r a c t p r o t e c t e d f u n c t i o n e x e c ( $ r e q u e t e ) ;
/ / M é t h o d e s p u b l i q u e s
/ / M é t h o d e d ’ e x é c u t i o n d ’ u n e r e q u ê t e
p u b l i c f u n c t i o n e x e c R e q u e t e ( $ r e q u e t e )
{
i f ( ! $ r e s u l t a t = $ t h i s−>exec ( $ r e q u e t e ) )
t h r o w new E x c e p t i o n
( " P r o b l è m e d a n s l ’ e x é c u t i o n de l a r e q u ê t e : $ r e q u e t e < b r / > " $ t h i s−>messageSGBD ( ) ) ;
r e t u r n $ r e s u l t a t ;
}
/ / M é t h o d e s a b s t r a i t e s
/ / A c c è s à l a l i g n e s u i v a n t e , s o u s f o r m e d ’ o b j e t
a b s t r a c t p u b l i c f u n c t i o n o b j e t S u i v a n t ( $ r e s u l t a t ) ;
/ / A c c è s à l a l i g n e s u i v a n t e , s o u s f o r m e d e t a b l e a u a s s o c i a t i f
a b s t r a c t p u b l i c f u n c t i o n l i g n e S u i v a n t e ( $ r e s u l t a t ) ;
/ / A c c è s à l a l i g n e s u i v a n t e , s o u s f o r m e d e t a b l e a u i n d i c é
a b s t r a c t p u b l i c f u n c t i o n t a b l e a u S u i v a n t ( $ r e s u l t a t ) ;
/ / E c h a p p e m e n t d e s a p o s t r o p h e s e t a u t r e s p r é p a r a t i o n s à
/ / l ’ i n s e r t i o n
a b s t r a c t p u b l i c f u n c t i o n p r e p a r e C h a i n e ( $ c h a i n e ) ;
/ / R e t o u r du m e s s a g e d ’ e r r e u r
a b s t r a c t p u b l i c f u n c t i o n messageSGBD ( ) ;
/ / F i n d e l a c l a s s e
}
La définition d’une classe abstraite est préfixée par le mot-clé abstract, de même que toutes les méthodes de la classe pour lesquelles seule la signature est donnée Toute classe PHP comprenant au moins une méthode abstraite doit elle-même être déclarée comme abstraite
Trang 5REMARQUE –La notion de classe abstraite existe depuis PHP 5, de même que celle
d’interface, concept assez proche mais encore un peu plus générique, que nous ne présentons
pas ici.
Dans la classe BD toutes les méthodes sont abstraites, à l’exception du construc-teur et de la méthode execRequete() qui exécute une requête Ces deux méthodes montrent comment répartir les tâches entre classe abstraite et classe dérivée :
1 tout ce qui est commun à l’ensemble des SGBD doit être factorisé au niveau
de la classe abstraite : ici il s’agit de la réaction à adopter si une connexion
ou l’exécution d’une requête échoue (on a choisi en l’occurrence de lever une exception) ;
2 tout ce qui est spécifique à un système particulier doit être dévolu à une
méthode abstraite qui devra être implantée au niveau de chaque classe dérivée (ici on a donc deux méthodes abstraites connect() et exec() destinées
à fournir respectivement le code de connexion et d’exécution de requêtes propres à chaque système)
La mention protected introduite ici est une variante de private Elle signifie que la méthode ou la propriété est invisible de tout script appelant (comme si elle était privée) mais accessible en revanche à toute sous-classe qui peut donc la surcharger Toute propriété ou méthode déclarée private n’est accessible que dans la classe qui la définit La méthode exec() par exemple doit être déclarée protected pour pouvoir être redéfinie au niveau des sous-classes de BD
Il est impossible d’instancier un objet d’une classe abstraite Celle-c n’est d’une certaine manière qu’une spécification contraignant l’implantation des classes déri-vées Pour être instanciables, ces classes dérivées doivent impérativement fournir une implantation de toutes les méthodes abstraites Voici la classe BDMySQL (à comparer avec la classe MySQL, page 121)
Exemple 3.4 exemples/BDMySQL.php:La classe dérivée BDMySQL
<? php
/ / S o u s−c l a s s e de l a c l a s s e a b s t r a i t e BD, i m p l a n t a n t l ’ a c c è s à / / MySQL
r e q u i r e _ o n c e ( "BD php " ) ;
c l a s s BDMySQL e x t e n d s BD
{
/ / P a s d e p r o p r i é t é s : e l l e s s o n t h é r i t é e s d e l a c l a s s e BD
/ / P a s d e c o n s t r u c t e u r : l u i a u s s i e s t h é r i t é
/ / M é t h o d e c o n n e c t : c o n n e x i o n à MySQL
p r o t e c t e d f u n c t i o n c o n n e c t ( $ l o g i n , $ m o t _ d e _ p a s s e , $ b a s e ,
$ s e r v e u r )
{
/ / C o n n e x i o n au s e r v e u r MySQL