Il y a longtemps que je n’ai pas écrit sur le langage ASP. Le ASP.NET est sorti il y a maintenant 20 ans, l’ASP classique est dépassé! Alors pourquoi en parler ? Je ne compte pas le nombre de sites dédiés à l’ASP qui ont disparu. L’excellent learnasp.com si je me souviens bien, 15seconds.com ou le site francophone asp-magazine.com... Si l’ASP n’a pas changé durant toutes ces années, le serveur IIS lui a changé ! Beaucoup de programmeurs l’utilisent toujours, moi le premier. Et n’oublions pas que la dernière version de IIS (2016) (M.A.J. 2019) supporte toujours l’ASP classique !
Personnellement, j’aime bien l’environnement, moins lourd que le .NET. Bon il a ses lacunes, notamment au niveau des fichiers UTF-8, mais aussi ses solutions ! Il est aussi plus simple ou plus à nu je dirais ! Et surtout, le langage ASP avait déjà un avantage de taille, la gestion de document XML. Le langage PHP (un an plus vieux que le ASP) a dû attendre la version PHP5 (2004-2008) pour gérer les objets XML. À l’époque l’Action Script de Macromedia, que j’utilisai beaucoup, avait aussi un semblant d’objet XML.
On oublie souvent que même s’il n’est pas considéré comme tell, l’ASP est aussi un langage de « Programmation Orientée Objet (POO) » pour peu qu’on lui donne l’occasion ! Plutôt rare dans les forums dédiés ou les blogues, la classe ASP est de toute façon un exemple de bonne pratique de programmation. Les classes ont l’avantage d’organiser et de structurer votre code encapsulé. C’est souvent lors de la création de classe ou d’objet que les subtilités de votre architecture se montrent au grand jour. En plus, une classe est réutilisable, simple comme bonjour et sémantique (human-readable) par dessus le marcher ! Même si vous ne connaissez pas l’ASP classic, ça reste un classique ;- )
Les exemples de classes trouvées sur le Web utilisent souvent une connexion (ADO) à une base de données avec le prétexte de simplifier le processus en éliminant une grande quantité de code. Ce n’est peut-être pas le meilleur exemple. En ASP il y a la méthode .getrow qui permet de récupérer un jeu d’enregistrement de la base de données dans une matrice (array). C’est même la manière la plus efficace (en ASP) et la moins énergivore de récupérer les informations stockées dans la base de données. Encapsulez le processus dans une fonction et vous aurez le même résultat ! On dit aussi que la classe est plus facile à lire. Encore une fois c’est vrai, une matrice utilise des numéros. Mais remplacer les numéros par des constantes en bon français et vous aurez le même résultat.
L’utilisation des classes semble pratiquement inutile dans ce contexte. En d’autres mots, vous pouvez ignorer les classes pendant toute votre vie sans problème. C’est donc quasiment une philosophie, mais les classes restent la meilleure manière de programmer pas seulement pour les puristes. Personnellement, j’utilise beaucoup les classes pour éviter d’avoir à créer une base de données justement ! Je vais vous donner un exemple plus simple.
J’ai des enfants inscrits au cap de jour. Je dois afficher leur nom et leur âge. Je stocke dans la base le nom et la date de naissance, mais c’est complètement inutile de sauvegarder l’âge de l’enfant qui varie selon le jour de l’année. Il suffit de créer une fonction pour calculer l’âge et le tour est joué. Transposons le tout dans une classe...
La classe ASP (ASP class)
Une classe est une façon de représenter un « objet » constitué de « propriétés » et de « méthodes ». De là l’expression « orientée objet ». C’est tout ! À la base, l’essentiel de la classe, c’est l’objet avec ses caractéristiques. Et ses caractéristiques sont en termes de programmation identique a des variables et des fonctions ou sub qu’on aurait incorporées ou encapsulé dans une classe. D’ailleurs, on utilise tous les jours des classes ASP sans le savoir, l’instruction ou l’objet « Response » est une classe. « write » est une méthode alors que « expiresabsolute » est une propriété de la classe « Response ».
Le ASP a six objets intégrés :
- Application
- Request
- Response
- Session
- Server
- ASPError
Pour suivre notre exemple et revenir à nos moutons, les propriétés sont le nom et la date de naissance de l’enfant. Puisque je veux récupérer le nom, la propriété doit être « Public », mais je n’ai pas besoin de la date de naissance qui servira seulement à faire le calcul, alors je la déclare « Private ». Une propriété (ou une méthode) déclarée en « Private » peut être utilisée seulement à l’intérieur de la classe. Noter aussi l’usage du souligné après la propriété privée, une convention pour identifier les propriétés privées.
class maClasseEnfant Public nom end class
Exemple
Il n’y a aucun paramètre contrairement à une fonction. Pour l’utiliser et la manipuler, il faut créer une instance et c’est là que ça devient intéressant :
class maClasseEnfant Public nom end class dim monObjetEnfant ' Créer une instance de la classe Set monObjetEnfant = New maClasseEnfant ' Assigner une valeur à la propriété « nom » monObjetEnfant.nom = "Pierre" ' Afficher la propriété « nom » response.write monObjetEnfant.nom ' détruire l’objet ou l’instance Set monObjetEnfant = Nothing
On peut avoir deux instances de la même classe.
class maClasseEnfant Public nom end class dim monObjetEnfant1, monObjetEnfant2 Set monObjetEnfant1 = New maClasseEnfant Set monObjetEnfant2 = New maClasseEnfant monObjetEnfant1.nom = "Pierre" monObjetEnfant2.nom = "Luc" ... Set monObjetEnfant1 = Nothing Set monObjetEnfant2 = Nothing
Simple référence
La façon la plus simple d’utilise une classe est comme simple référence. Au lieu d’utiliser des variables ou des constantes par exemple, j’organise souvent mes variables globales ou de session et même certaines fonction en classe. Ça permet d’encapsuler certaines opérations directement dans la classe et d’avoir des variables très explicites et des modules d’un grande portabilité...
En programmation, on a souvent le dilemme suivant, dois-je créer une base de données ou non ? Est-ce que ça en vaut la peine. Parfois, un simple fichier avec des variables suffit, parfois un fichier XML ou JSON. Il y a aussi un adage qui dit en programmation, si tu n’en as pas besoin maintenant c’est que tu n’en auras jamais besoin. Ou simplement, le développement requiert du temps, beaucoup de temps. On peut en sauver en laissant certaines étapes du développement lors de phase à venir.
La classe ne se préoccupe pas d’où proviennent les données. Alors peut importe quelles proviennent de variables, d’un fichier ou d’une base de données, vous pouvez changer à volonté la source sans avoir a modifier l’architecture de votre classe et donc une seule ligne de code de votre programme. Présentement, je développe une application et c’est exactement ce que je fais. Au lieu de me connecter à la base de données pour recueillir les données d’à peine 10 enregistrements avec des données tout ce qui a de plus stables. J’utilise un simple « Select Case » comme base de données, mais une classe pour stocker ces données. Lorsque le temps viendra, je substituerais le « Select case » par une connexion à la base de données tout simplement. Enfin beaucoup de mots pour faire :
class configuration ' Variables publiques public no public titre public code public prefix end class dim oConfig, nEnregistrement nEnregistrement = 2 set oConfig = new configuration select case nEnregistrement case "1" : oConfig.no = 1 oConfig.titre = "Titre 1" oConfig.code = "12001" oConfig.prefix = "PRE1" case "2" : oConfig.no = 2 oConfig.titre = "Titre 2" oConfig.code = "12002" oConfig.prefix = "PRE2" case else : oConfig.no = 0 oConfig.titre = "Aucun titre" oConfig.code = "" oConfig.prefix = "" end select response.write "<br />" & oConfig.no response.write "<br />" & oConfig.titre response.write "<br />" & oConfig.code response.write "<br />" & oConfig.prefix set oConfig = nothing
Il va s’en dire que les puristes n’aimeront pas, mais c’est tout à fait possible en ASP, je l’utilise tous les jours. Avouons que « oConfig.prefix » est plus élégant et plus structuré que « maConstanteConfigPrefix » ! Voyons maintenant le véritable rôle et surtout la bonne manière d’écrire une classe pour le pauvre puriste que je suis ; -)
La propriété, le Let et le Get
Comme on peut voir, il suffit d’assigner une valeur à la propriété nom sans autre complication. Ça semble exagéré comme technique, mais c’est une très bonne façon de comprendre le processus derrière la classe ASP. C’est même fortement conseillé, car les variables de classe devraient idéalement être accessibles dans la hiérarchie d’une ou plusieurs méthodes de la classe.
class maClasseEnfant Private nom_ Public Property get nom nom = nom_ End Property Public Property let nom(v) nom_ = v End Property end class dim monObjetEnfant Set monObjetEnfant = New maClasseEnfant monObjetEnfant.nom = "Pierre" response.write monObjetEnfant.nom Set monObjetEnfant = Nothing
La classe est ici un peu plus complexe, quoique plus explicative. Mais son utilisation n’a pas changé du tout. Maintenant, ajoutons la date de naissance de la même manière et pourquoi pas un véritable nom cette fois !
class maClasseEnfant Private nom_ Private prenom_ Private naissance_ Public Property get nom nom = prenom_ & " " & nom_ End Property Public Property let nom(v) nom_ = v End Property Public Property let prenom(v) prenom_ = v End Property Public Property Let naissance(v) naissance_ = cDate(v) end property end class dim monObjetEnfant Set monObjetEnfant = New maClasseEnfant monObjetEnfant.nom = "Cardin" monObjetEnfant.prenom = "Pierre" monObjetEnfant.naissance = "2013/12/01" response.write monObjetEnfant.nom Set monObjetEnfant = Nothing
Rien de nouveau si ce n’est que je profite de l’occasion pour convertir la chaine envoyée en date (cDate), au cas où. Mais regarder bien l’exemple, en particulier la propriété « nom ». On a un bon exemple de séparation entre les propriétés et les méthodes. La propriété « nom » n’est pas la même que la méthote Let nom(v) qui assigne une valeur la la propriété privée nom_! Ce qui me permet d’afficher la propriété « nom » sans conflit. Il va s’en dire que l’idéale est de modifier le nom de la propriété pour la distinguer, exactement comme le souligner_ après une propriété privée. Il n’y a pas de convention à ma connaissance mais « nom_complet » me semble faire l’affaire !
class maClasseEnfant Private nom_ Private prenom_ Private naissance_ Private age_ Public Property Let prenom(v) prenom_ = v End Property Public Property Let nom(v) nom_ = v End Property Public Property Let naissance(v) naissance_ = cDate(v) End Property Public Property Get nom_complet nom_complet = prenom_ & " " & nom_ End Property Public Property Get age age = DateDiff("yyyy",naissance_,date()) & " ans en " & year(date) End Property end class dim monObjetEnfant Set monObjetEnfant = New maClasseEnfant monObjetEnfant.prenom = "Pierre" monObjetEnfant.nom = "Jean" monObjetEnfant.naissance = "2013/12/01" response.write monObjetEnfant.nom_complet & " à " & monObjetEnfant.age Set monObjetEnfant = Nothing
Méthode « Sub »
Bon l’exemple ne calcule pas vraiment l’âge réel, c’est un exemple pour ne pas perdre de vu la structure d’une classe. Et encore une fois, les puristes préfèreront utiliser une fonction privée (sub) pour faire la même chose :
class maClasseEnfant Private nom_ Private prenom_ Private naissance_ Private age_ Public Property Let prenom(v) prenom_ = v End Property Public Property Let nom(v) nom_ = v End Property Public Property Get age calculdelage() age = age_ End Property Public Property Let naissance(v) naissance_ = cDate(v) End Property Public Property Get nom_complet nom_complet = prenom_ & " " & nom_ End Property Private Sub calculdelage() dim nAge nAge = DateDiff("yyyy",naissance_,date()) if nAge > 1 then age_ = nAge & " ans en " & year(date) else age_ = nAge & " an en " & year(date) end if End Sub End class dim monObjetEnfant Set monObjetEnfant = New maClasseEnfant monObjetEnfant.prenom = "Pierre" monObjetEnfant.nom = "Jean" monObjetEnfant.naissance = "2013/12/01" response.write monObjetEnfant.nom_complet & " à " & monObjetEnfant.age Set monObjetEnfant = Nothing ' résultat Pierre Jean à 7 ans en 2020
Méthodes natives
On pourrait aussi placer notre code qui calcul l’âge dans une seule fonction publique au lieu d’utiliser un « sub » et une propriété. Mais mine de rien, on vient de faire le tour des plus importantes fonctionnalités de la classe ASP. Tout, du plus simple au plus complexe des codes utilisera toujours plus ou moins la même syntaxe. Maintenant, pour vraiment faire le tour des fonctionnalités des classes, il faut parler de quelques fonctions ou méthodes natives :
class maClasse Private Sub Class_Initialize() ' Exécuté automatiquement lors de la ' création d’une instance de cette classe. ' Par exemple initialiser des constantes ' ou une connexion à la base de donnée... End Sub Private Sub Class_Terminate() ' Exécuté lors de la fin de la classe ' Par exemple fermer la connexion ' à la base de données End Sub end class
Passer une classe de page en page
Dans les nombreuses possibilités, il y a la capacité d’une classe de passer les données d’une session de page en page. On exploitera ici la variable « session ». Il suffit de stocker dans la variable « session », à l’aide d’une méthode les propriétés et un séparateur. Propriétés qu’une seconde méthode pourra extraire d’un paramètre provenant de ladite variable « application » afin de les réassigner à la nouvelle entité. C’est plus simple à faire qu’à dire ; -)class maClasseEnfant ... Public Property Get session session = prenom_ & Chr(1) & nom_ & Chr(1) & naissance_ End Property Public Property Let session(v) Dim aSession aSession = Split(v, Chr(1)) If (IsArray(aSession) AND (UBound(aSession) >= 2)) Then prenom_ = aSession(0) nom_ = aSession(1) naissance_ = aSession(2) End If End Property End class Il suffit de sauver la classe dans la variable session : dim monObjetEnfant Set monObjetEnfant = New maClasseEnfant Session("maSession") = monObjetEnfant.session ... Et dans la nouvelle page, récupérer la classe à partir de la variable session : dim monObjetEnfant Set monObjetEnfant = New maClasseEnfant monObjetEnfant.session = Session("maSession") response.write monObjetEnfant.nom_complet & " à " & monObjetEnfant.age Set monObjetEnfant = Nothing
Code complet
class maClasseEnfant Private nom_ Private prenom_ Private naissance_ Private age_ Public Property Let prenom(v) prenom_ = v End Property Public Property Let nom(v) nom_ = v End Property Public Property Get age calculdelage() age = age_ End Property Public Property Let naissance(v) naissance_ = cDate(v) End Property Public Property Get nom_complet nom_complet = prenom_ & " " & nom_ End Property Private Sub calculdelage() dim nAge nAge = DateDiff("yyyy",naissance_,date()) if nAge > 1 then age_ = nAge & " ans en " & year(date) else age_ = nAge & " an en " & year(date) end if End Sub Public Property Get session session = prenom_ & Chr(1) & nom_ & Chr(1) & naissance_ End Property Public Property Let session(v) Dim aSession aSession = Split(v, Chr(1)) If (IsArray(aSession) AND (UBound(aSession) >= 2)) Then prenom_ = aSession(0) nom_ = aSession(1) naissance_ = aSession(2) End If End Property end class dim monObjetEnfant Set monObjetEnfant = New maClasseEnfant if Session("maSession") <> "" then monObjetEnfant.session = Session("maSession") else monObjetEnfant.prenom = "Pierre" monObjetEnfant.nom = "Jean" monObjetEnfant.naissance = "2013/12/01" Session("maSession") = monObjetEnfant.session end if response.write monObjetEnfant.nom_complet & " à " & monObjetEnfant.age& "<br />" Set monObjetEnfant = Nothing
Matrice (array) d’objets
La classe est un outil très puissant. Comme on a vu, on peut l’utiliser comme simple référence, comme moyen de passer une session de page en page et même pour créer une entité d’un enregistrement à partir d’une base de données. Mais on peut aussi sauver plusieurs entités dans une seule classe ! C’est-à-dire s’en servir comme matrice.
L’idée c’est une classe à l’intérieur d’une autre classe (enfant dans famille) est bien sûr d’utiliser une matrice (array) dans la classe « famille ». Comme on doit le faire en ASP, il faut redimensionner la matrice à chaque fois (ReDim Preserve). Noter l’utilisation de la méthode « Class_Initialize » et la propriété « date » pour créer une classe en une seule instruction.
<% Class Enfant Private nom_ Private prenom_ Public Property Get nom() nom = nom_ End Property Public Property Let nom(nomParam) nom_ = nomParam End Property Public Property Get prenom() prenom = prenom_ End Property Public Property Let prenom(prenomParam) prenom_ = prenomParam End Property Public property Get data data = nom_ & Chr(1) & prenom_ End Property ' Propriété pour créer un enfant en une seule instruction Public Property Let data(v) dim aData aData = Split(v, Chr(1)) if (IsArray(aData) AND (ubound(aData) >= 1)) then nom_ = aData(0) prenom_ = aData(1) end If End Property End Class Class Famille Private Items_ Public Property Get Items() Items = Items_ End Property Public Property Set Items(itemsParam) Set Items_ = itemsParam End Property Private Sub Class_Initialize Items_ = Array() End Sub Sub AddEnfant(newitem) ReDim Preserve Items_(UBound(Items_) + 1) set Items_(UBound(Items_)) = newitem End Sub End Class Dim oEnfant, oFamille Set oFamille = New Famille Set oEnfant = new Enfant oEnfant.nom = "Pierre" oEnfant.prenom = "Savard" oFamille.AddEnfant(oEnfant) Set oEnfant = Nothing Set oEnfant = new Enfant ' Plus simple, utiliser la propriété pour créer un enfant oEnfant.data = "Luc" & Chr(1) & "Tremblay" oFamille.AddEnfant(oEnfant) Set oEnfant = Nothing Set oEnfant = new Enfant oEnfant.data = "Jean" & Chr(1) & "Lafontaine" oFamille.AddEnfant(oEnfant) Set oEnfant = Nothing dim oEnfantListe For each oEnfantListe in oFamille.Items Response.write "<br>" & oEnfantListe.nom & " " & oEnfantListe.prenom Next %>
Dictionnaire (Scripting.Dictionary) d’objets
Un peu comme la matrice (array), en ASP il y a l’objet « Dictionnaire » qui permet d’emmagasiner des valeurs et même des objets dans une combinaison de clés uniques et d’éléments ou des paires nom/valeur. Voici le même exemple avec un dictionnaire au lieu de la matrice. En place et lieu de la classe « Famille », on a un dictionnaire « famille » (oFamille). En fait, c’est exactement comme la classe, mais sans devoir redimensionner la matrice ni créer les propriétés qui sont déjà prêtes à l’emploi, comme l’élément « item », tous les éléments « Items », toutes les clés « Keys », ajouter un élément « add », modifier un élément « key », supprimer un élément « Remove », tout supprimer « RemoveAll », compter le nombre d’éléments « count », et tester si une clé existe « Exists ».
<%
Class Enfant
Private nom_
Private prenom_
Public Property Get nom()
nom = nom_
End Property
Public Property Let nom(nomParam)
nom_ = nomParam
End Property
Public Property Get prenom()
prenom = prenom_
End Property
Public Property Let prenom(prenomParam)
prenom_ = prenomParam
End Property
Public property Get data
data = nom_ & Chr(1) & prenom_
End Property
Public Property Let data(v)
dim aData
aData = Split(v, Chr(1))
if (IsArray(aData) AND (ubound(aData) >= 1)) then
nom_ = aData(0)
prenom_ = aData(1)
end If
End Property
End Class
Dim oEnfant, oFamille
Set oFamille = Server.CreateObject("Scripting.Dictionary")
Set oEnfant = new Enfant
oEnfant.data = "Pierre" & Chr(1) & "Savard"
oFamille.Add 0,oEnfant
Set oEnfant = Nothing
Set oEnfant = new Enfant
oEnfant.data = "Luc" & Chr(1) & "Tremblay"
oFamille.Add 1,oEnfant
Set oEnfant = Nothing
Set oEnfant = new Enfant
oEnfant.data = "Jean" & Chr(1) & "Lafontaine"
oFamille.Add 2,oEnfant
Set oEnfant = Nothing
' Boucle tous les enfants
dim oEnfantListe
For each oEnfantListe in oFamille.Items
Response.write "<br>" & oEnfantListe.nom & " " & oEnfantListe.prenom
Next
' Tests
if oFamille.Exists(0) then
response.write "<br>" & oFamille.Item(0).nom
else
response.write "<br>L’élément « 0 » n’existe pas!"
end if
if oFamille.Exists(1) then
response.write "<br>" & oFamille.Item(1).nom
else
response.write "<br>L’élément « 0 » n’existe pas!"
end if
if oFamille.Exists(2) then
response.write "<br>" & oFamille.Item(2).nom
else
response.write "<br>L’élément « 0 » n’existe pas!"
end if
' Exemple d’item qui n’existe pas!
if oFamille.Exists(3) then
response.write "<br>" & oFamille.Item(3).nom
else
response.write "<br>L’élément « 0 » n’existe pas!"
end if
%>
Vous avez des manipulations à faire, imaginer des relations entre les enfants, entre plusieurs familles. Ou encore des fonctionnalités, comme calculer l’âge, etc. Il suffit de créer des fonctions dans des propriétés de la classe et tout est bien encapsulé dans un seul objet. Sauvez le code dans un fichier et il suffira de l’inclure dans vos projets sans avoir à le réinventer. Un bon exemple est une classe d’usager avec mot de passe, cryptage, gestion des sessions et système de connexion. Il suffit d’inclure le fichier des classes comme une librairie de classes, de créer les objets et bingo !
En conclusion
Il n’y a rien de bien pratique, ça reste un exemple pour démontrer le potentiel de la classe ASP. Un bon code pratique est « The bookstore example » par Richard Quinn : « Object Oriented ASP: Using Classes in Classic ASP ». Des classes relationnelles à partir d’une base de données pour vous montrer l’étendue des possibilités. Comme je disais plus haut, l’avantage n’est pas nécessairement dans l’encapsulation de ce processus, mais plutôt dans l’art de la programmation orientée objet qui est intéressant. À le lire, il ne semble ne pas connaitre les nombreuses possibilités de l’objet ADO, un vrai politicien ;- ) D’ailleurs il parle du « paquet de troubles » qu’implique la programmation imbriquée dans du HTML sans proposer de solution !
Je n’ai pas trouvé de référence en français ! Le PHP c’est 80% de la programmation côté serveur. C’est pour cette raison que j’ai pris le temps d’écrire ce petit tutoriel. Je suis même tombé sur une bête traduction ou la mnémonique « Set » avait été traduite par « Ensemble » !
Référence
- Object Oriented ASP: Using Classes in Classic ASP par Richard Quinn
- Using Classes within VBScript par Mark Lidstone (4guysfromrolla.com)
- Un autre rare exemple de code complet par sebsworld.net
- ASP Dictionary Object par w3schools