vendredi 6 avril 2007

Interfaces & Classes Abstraites en LotusScript


Les classes abstraites et les interfaces définissent des propriétés et des méthodes, au même titre que les classes traditionnelles. Il est possible de créer autant d'objets que nécessaire à partir de classes standards, alors que les interfaces ne servent qu'à décrire les fonctions que couvriront de futurs objets. Généralement les interfaces ne contiennent pas de code et ne permettent pas d'instancier des objets. Les classes abstraites, quant à elles, prototypent des propriétés et des méthodes. Il n'est pas permis non plus d'instancier directement des classes abstraites, celles-ci doivent être dérivées afin que leurs sous-classes héritent de leur code. Les compilateurs contrôlent généralement la couverture de toutes les propriétés et méthodes et s'assurent que nul n'instancie des interfaces ou des classes abstraites.

D'ordinaire les concepteurs utilisent les interfaces et les classes abstraites pour construire des applications à faible couplage au code plus adaptable, plus maintenable et finalement plus robuste. Bien qu'absents du langage LotusScript, ces concepts, issus de la conception orientée objet, sont aisément implémentables en LotusScript.

Imaginez la classe LotusScript.io.File aux propriétés et méthodes suivantes: canWrite( ), getAbsolutePath( ), length( ), list( ), renameTo( ). Il est envisageable de la construire simplement en LotusScript, mais aussi avec LS2J en s'appuyant sur Java, comme d'utiliser le composant COM Windows Script Host ou bien d'appeler les API 32-bits natives de Windows. Il devient alors tentant de définir une interface iFile décrivant ces propriétés et méthodes indépendamment de la solution finale restant à valider. Vous fabriquez ainsi une application dépendant d'une seule interface stable, alors que l'implémentation concrète peut varier à loisir sans heurts pour le code appelant. Voilà une application Notes ou Domino faiblement couplée.

Figure 1: Le diagramme de classes UML de StopWatches

Le projet libre OpenDOM téléchargeable sur www.openntf.org fournit bon nombre d'exemples utilisant les notions de classe abstraite ou d'interface. La bibliothèque LotusScript.langStopWatches définit une interface publique iTicker à partir de laquelle quatre implémentations différentes ont été construites. Elles supportent LotusScript version 3 ou 4+ ainsi que Windows 16-bits ou 32-bits. Les paquetages, ou espaces de nommages, LotusScript.lang.Rn ou LotusScript.windows.winnn hébergent chacun une classe Ticker implémentant concrètement l'interface iTicker. La classe publique StopWatches charge dynamiquement le moteur, construit d'après iTicker, qui est le plus adapté au contexte courant.

Vous pouvez éviter l'instanciation d'objets et vous assurer que les propriétés et les méthodes sont surchargées ou dérivées. Mais créer un constructeur de classe LotusScript privé comme ..
Public Class iVehicle
Private Sub New
End Sub
End Class ' iVehicle
.. est refusé par le compilateur :
<notename>: <eventname>: <linenumber>: Illegal PRIVATE declaration of: NEW

Puisque LotusScript ne supporte pas les interfaces ou les constructeurs privés j'utilise une technique différente. Je m'assure qu'aucune instanciation d'interface ou de classe abstraite n'est réalisée à l'exécution seulement en lançant une exception au sein du constructeur de classe. Je force également la dérivation des propriétés et des méthodes par d'autres exceptions comme suit :
%INCLUDE "LsErr.lss"
Public Class iVehicle '
Public Sub New ' This forbids interface instantiation, please derive me !
If Typename( Me ) = "IVEHICLE" Then Error ErrNotAnObject
End Sub ' New
Public Property Get Color As NotesColor
Error errPropGetNotDefined
End Property ' Color
Public Property Set Price As Currency
Error errPropSetNotDefined
End Property ' Price
Public Sub starts( )
Error ErrNotAMethod
End Sub ' starts( )
End Class ' iVehicle

Public Class aShuttle
Public Property Get isFree As Boolean '
Error errPropSetNotDefined
End Property ' isFree
End Class ' aShuttle

Une fois mon interface décrite, je peux réaliser autant de classes concrètes qui l'implémentent et fournir une fabrique de classe ou une routine destinée à distribuer des objets différents selon le contexte :
Private Class Car As iVehicle
' Your code goes here ..
End Class ' Car As iVehicle

Private Class Truck As iVehicle
' Your code goes here ..
End Class ' Truck As iVehicle

Public Function createVehicle As iVehicle
If ( so And so ) Then
Set Me.createVehicle = New Car
Else
Set Me.createVehicle = New Truck
End If
End Function

La classe LotusScript.lang.Factory d'OpenDOM crée des objets de cette façon. Elle s'inspire d'un redbook d'IBM intitulé « Performance Considerations for Domino Applications » dont l'annexe « B-2 Dynamic Script Library Loading » décrit d'intelligente manière comment charger dynamiquement des bibliothèques LotusScript dans vos applications orientées objet. Toutefois le code fournit par IBM réalise celà à l'aide de variants, c'est à dire au dépend d'un typage fort des données contrôlable par le compilateur. L'ajout d'interface dans votre code fixe celà et a de multiples avantages :
  • Il est possible de concevoir des applications à faible couplage en LotusScript qui s'affranchissent des implémentations techniques sous-jacentes
  • Les données font l'objet d'un typage fort au moment de la compilation, pratique vivement recommandée
  • L'empreinte mémoire de l'interpréteur LotusScript augmente progressivement à mesure que les bibliothèques sont appelées. Une compilation initiale précède ces appels, mais elle n'est pas notable alors qu'elle est pleinement compensée par une utilisation mémoire minime.
Pour réaliser une reconnaissance des objets dynamiques à la compilation, la classe Factory utilise des interfaces ou des classes abstraites. A contrario de la routine NewObj d'IBM, la classe Factory instancie des objets fortement typés, le contrôle des types lors de l'écriture du code renforce la robustesse et la stabilité des applications.

Lectures annexes:

Aucun commentaire:

Locations of visitors to this page