Alors que la programmation orientée objet est devenue inévitable de nos jours, de nombreuses applications Notes & Domino demeurent écrites ou sont développées en utilisant une programmation procédurale traditionnelle.
Ce billet illustre comment les variables globales présentes dans les masques, les vues ou les bibliothèques de script peuvent profiter des techniques de modélisation et de programmation objet afin de produire des programmes plus sûrs.
Les variables globales servent à partager et à réutiliser les données ou routines entre masques, vues ou bibliothèques de script. Examinons les 3 variables que contient "ma Bibli" :
Dim directory As NotesDatabase
Dim user As NotesName
Dim location As NotesDocument
Ces trois variables sont initialisées dès que possible et représentent respectivement le carnet d'adresses d'entreprise, l'utilisateur courant et le contexte applicatif tel que "Bureau", "Déplacement", "Local" ainsi de suite. Les applications Domino peuvent réutiliser à faible coût les trois variables de "ma Bibli" au sein de masques, de vues ou d'agents. Cependant ces trois variables sont trop exposées, en effet tout développeur à tout moment, en tout lieu peut réinitialiser celles-ci par mégarde en codant par exemple :
Delete directory
' OU
Set user = Nothing
' OU
location.Remove True
De telles failles sont d'ordinaire détectées à l'exécution des tests mais parfois également en production. Ces trois instructions génèrent la trop fameuse exception "Object variable not set" lorsque l'on tente de référencer les variables directory, user ou bien location. Dans leur livre "Writing Secure Code", édité par Microsoft, Michael Howard et David LeBlanc mentionne ce point: « Ne supposez jamais que votre application s'exécutera dans un nombre limité de contextes. Il y a de grandes chances qu'un utilisateur l'exécutera dans un environnement encore inconnu de vous. Supposez au contraire que votre code s'exécutera dans un context des plus hostiles, et concevez, codez et testez vos programmes en conséquence ». Aussi désagréable que cette recommandation paraisse, une bonne nouvelle est que le langage LotusScript permet de réaliser cet objectif.
Une manière efficace de protéger nos variables est de les déclarer en lecture seule, malheureusement un telle instruction n'existe pas en LotusScript. Une solution à ce problème existe cependant depuis la première mouture du Langage LotusScript implémentée dans Notes version 4, bien qu'elle n'ait rarement sinon jamais été publiquement exposée sur la Toile. En effet, la documentation LotusScript dissimule le fait que les propriétés peuvent être implémentées dans les bibliothèques de script au même titre que les fonctions et les routines. Il n'est pas nécessaire de passer par une classe pour définir une propriété comme le montre le code suivant :
Property Get directory As NotesDatabase
Set directory = New NotesDatabase( <server>, <filepath> )
End Property ' directory
' OU
Private m_user As NotesName
Public Property Get user As NotesName
Dim session As New NotesSession
Set m_user = New NotesName( session.UserName )
Set user = m_user
End Property ' user
Lors d'un déréférencement accidentel de variables globales définies ainsi dans les bibliothèques de script, les développeurs sont alertés lors de la compilation. Ainsi l'instruction
Delete directory est interceptée par le compilateur qui affiche "Agent: Routine: n° ligne:
DELETE not valid on: DIRECTORY" alors que l'instruction Set user = Nothing génère le message "Bibliothèque: Routine: n° ligne: PROPERTY SET not defined for: USER", sauf à définir l'accesseur suivant :
Public Property Set user As NotesName
Set m_user = user
End Property ' user
Cette technique répond à toutes les situations excepté celle de
<object>.Remove. Des cas particuliers existent pour chaque application et nécessitent le recours à la programmation défensive afin de protéger les objets d'un déréférencement involontaire par instruction
Delete ou
Set , ou bien à travers la méthode
<object>.Remove.
En recourant aux propriétés dans les bibliothèques de script, il est possible d'implémenter une logique applicative complexe :
- La propriété "directory" peut traduire une recherche au sein d'une concaténation de carnet d'adresses d'entreprise ou bien des répertoires agrégés et/ou partiels
- La propriété "location" peut se ré-initialiser seule chaque fois que nécessaire alors que le dé-référencement par location.remove demeure indétecté à la compilation comme à l'exécution :
Public Property Get location As NotesDocument
If m_location Is Nothing Then ' instancions une copie mémoire de location
Set m_location = directory.createDocument
Call directory.GetView( "Locations" ).getDocumentByKey( <Value> ).CopyAllItems( m_location )
End If
Set location = m_location
End Property ' location
N'attendez pas plus longtemps ! Introduisez dès maintenant ces pratiques simples de la programmation orientée objet au sein de vos bibliothèques de scripts en code procédural !