Eiffel complète sur le .NET Framework

 

Raphael Simon
Emmanuel Stapf
Ingénierie logicielle interactive
Santa Barbara, Californie

Bertrand Meyer
ETH (Swiss Federal Institute of Technology)
Zürich, Suisse

Juillet 2002

Résumé: Décrit l’implémentation et l’intégration de toute la puissance du langage et de la méthode Eiffel, y compris la conception par contrat, l’héritage multiple, la générique et d’autres fonctionnalités avancées, dans Microsoft .NET Framework, en créant un environnement qui fournit une solution de pointe pour les développeurs de logiciels Internet ambitieux. (27 pages imprimées)

Note Une version antérieure de cet article a été publiée en juillet 2000 sous le titre Eiffel on the Web: Integrationing Eiffel Systems into the Microsoft .NET Framework (par les auteurs actuels et Christine Mingins). La version actuelle décrit l’état actuel de l’implémentation, en prenant en charge la langue Eiffel complète.

Contenu

Introduction
Eiffel et .NET Framework : vue d’ensemble
À propos d’Eiffel
Fonctionnement d’Eiffel dans le .NET Framework
Tirer parti des mécanismes .NET Framework dans Eiffel
Production de systèmes .NET Framework à partir d’Eiffel
Conclusion
Références

Introduction

L’un des aspects les plus intéressants de Microsoft® .NET Framework est la base commune qu’il fournit pour l’implémentation de nombreux langages de programmation différents. L’une des premières technologies de langage à tirer parti de cette ouverture a été Eiffel, dont l’implémentation par l’ingénierie logicielle interactive (ISE) a été, dans une première version, présentée lors de la toute première introduction publique du .NET Framework à Orlando. Cette version initiale comportait une version partielle de la langue Eiffel, Eiffel#, décrite dans la version d’origine de cet article, qui est désormais obsolète. ISE a maintenant terminé l’implémentation complète d’Eiffel sur le .NET Framework et une première intégration à Microsoft® Visual Studio® .NET.

Eiffel pour .NET est un produit publié, disponible dans le cadre de la livraison ISE Eiffel à partir de la version 5.0 (la version actuelle au moment de la rédaction est 5.1).

Eiffel et .NET Framework : vue d’ensemble

Eiffel pour .NET est une implémentation complète de la méthode Eiffel et du langage exécutés sur Microsoft .NET Framework.

Eiffel est un environnement complet de développement logiciel (ISE Eiffel) basé sur une méthode qui couvre l’ensemble du cycle de vie des logiciels, non seulement l’implémentation, mais aussi l’analyse, la conception et la maintenance. L’environnement est basé sur le langage Eiffel, appliquant à fond les principes de la technologie d’objet et implémentant les concepts de Design by Contract™ pour produire des applications hautement fiables, extensibles et réutilisables.

ISE Eiffel est particulièrement destiné aux systèmes volumineux et complexes et est utilisé par les grandes organisations de l’industrie financière, de la défense, du temps réel et d’autres secteurs pour des développements stratégiques. Les universités du monde entier utilisent également Eiffel pour enseigner la programmation et le génie logiciel à tous les niveaux.

Le .NET Framework est le nouveau modèle de programmation de Microsoft pour la création d’applications web, d’applications clientes intelligentes et de services web XML, des applications qui exposent leurs fonctionnalités par programmation sur un réseau à l’aide de protocoles standard tels que SOAP, XML et HTTP. La spécification du .NET Framework est désormais une norme internationale, grâce à la soumission réussie par Microsoft de l’infrastructure de langage commun (CLI) aux normes ECMA organization, qui l’a adoptée en décembre 2001. (L’un des auteurs, Emmanuel Stapf de l’ISE, est membre du comité technique ECMA correspondant.) Bien qu’une présentation détaillée du .NET Framework dépasse le cadre de cet article, nous pouvons noter les points saillants suivants, qui intéressent particulièrement les développeurs d’applications :

  • L’architecture s’appuie sur une machine virtuelle, de sorte que les compilateurs pour n’importe quel langage génèrent toujours le même code, MSIL (Microsoft Intermediate Language).
  • Le code qui est exécuté sur n’importe quel ordinateur réel est du code natif (binaire) pour cet ordinateur, traduit de manière incrémentielle ou non, par le biais d’un processus appelé compilation juste-à-temps (JIT)
  • L’équivalent d’un système d’exploitation de la machine virtuelle est le Common Language Runtime (CLR), qui fournit un certain nombre de fonctionnalités cruciales (gestion de la mémoire, garbage collection, sécurité, gestion des exceptions) aux programmes, quelle que soit leur langue ou leur origine (d’où le mot « commun »).
  • Le modèle de mémoire utilisé par la machine virtuelle et le CLR ne repose pas sur les adresses, les octets et les mots ; au lieu de cela, il s’agit d’un modèle orienté objet basé sur les notions de type, de classe, d’objet, d’héritage, de polymorphisme, de saisie et d’appels liés dynamiquement.
  • Les mécanismes d’interopérabilité du langage du .NET Framework, y compris le Common Language Runtime, MSIL, le modèle objet et la spécification CLS (Common Language Specification), permettent aux différentes parties d’une application d’utiliser différents langages de programmation ( chacun choisi pour être le meilleur pour le travail en cours) et d’atteindre un degré de coopération inter langues sans précédent dans le monde des logiciels. Non seulement un module peut appeler une routine écrite dans un autre module, mais une classe dans un langage orienté objet peut hériter d’une classe dans un autre, les exceptions franchissent les limites linguistiques, comme le font les sessions de débogage, et tout cela est réalisé sans aucun effort spécial de la part du programmeur et sans avoir besoin que les langues se connaissent les unes les autres.
  • Un nouvel environnement de développement, Visual Studio .NET, fournit des fonctionnalités de développement avancées (compilation, navigation, débogage, développement d’interface utilisateur) et est, comme le reste de la technologie, ouvert à de nombreux langages.
  • .NET fournit des milliers de composants réutilisables s’étendant sur de nombreux domaines d’application, de la localisation à la mise en réseau et à l’analyse linguistique.
  • Parmi les bibliothèques de composants les plus importantes figurent Microsoft® ASP.NET, une infrastructure innovante pour la création d’applications web et de services Web XML ; Microsoft® ADO.NET, une bibliothèque d’interface relationnelle d’objet ; et Microsoft Windows® Forms pour la création d’applications clientes intelligentes basées sur Windows.
  • Ces mécanismes sont potentiellement disponibles pour les développeurs utilisant n’importe quel langage de programmation, à condition que les implémenteurs de ce langage offrent un compilateur compatible avec .NET Framework, non seulement en générant IL, mais également en respectant les règles d’interopérabilité du langage .NET Framework.

Le .NET Framework est attrayant pour les utilisateurs d’Eiffel, car il suit bon nombre des idées qu’ils ont acceptées comme essentielles au développement de logiciels de qualité : utilisation d’un modèle objet, nettoyage automatique de la mémoire et gestion des exceptions. Il offre également une plateforme intégrée avec un accès direct à des milliers de composants réutilisables, la perspective d’une interopérabilité totale avec les éléments logiciels écrits en Eiffel et dans d’autres langages, ainsi que la puissance des services Web XML et d’autres applications Internet.

Pour les utilisateurs du .NET Framework, Eiffel fournit la valeur ajoutée d’une méthode et d’un langage orienté objet avancés qui couvrent l’ensemble du cycle de vie, pas seulement la programmation, mais aussi l’ensemble du processus, en commençant par l’analyse et la conception et en continuant avec l’implémentation, la réutilisation, l’extension et la maintenance. Eiffel offre également des mécanismes de fiabilité uniques tels que Design by Contract™, des fonctionnalités de langage avancées telles que la générique et l’héritage multiple, ainsi que le vaste ensemble de composants réutilisables développés par ISE et d’autres parties, y compris la bibliothèque EiffelBase de structures de données et d’algorithmes et la bibliothèque EiffelVision pour les graphiques multiplateformes.

Eiffel sur le .NET Framework offre une combinaison idéale pour les entreprises qui souhaitent tirer parti des meilleures technologies dans les systèmes d’exploitation, l’infrastructure Internet et Web, les méthodes de développement de logiciels et les environnements de développement. En particulier, l’ouverture d’Eiffel à d’autres langages et environnements, combinée à l’accent mis par .NET Framework sur la neutralité linguistique, fait du produit résultant un véhicule idéal pour la création d’applications contenant des composants dans de nombreuses langues différentes, Eiffel servant de colle entre eux. Dans le reste de cet article, nous décrivons cette combinaison et les défis que nous avons rencontrés lors de l’intégration d’ISE Eiffel dans .NET.

À propos d’Eiffel

Étant donné que le reste de ce document définit Eiffel pour .NET Framework en décrivant en quoi il diffère d’Eiffel, nous devons d’abord voir les caractéristiques main d’Eiffel. Vous trouverez plus de détails dans le livre Object-Oriented Software Construction, 2e édition [Meyer 1997] et Eiffel: The Language [Meyer 1992], ainsi que sur le site Web Eiffel à l’adresse http://www.eiffel.com/, d’où une partie du matériel a été extraite.

Eiffel est la combinaison de quatre éléments :

  1. Une méthode de développement de système, basée sur des principes d’ingénierie logicielle forts.
  2. Langue prenant en charge la méthode .
  3. Un environnement de développement, ISE EiffelStudio.
  4. Ensemble complet de bibliothèques réutilisables.

Méthode

La méthode, le langage et l’environnement Eiffel mettent l’accent sur le développement continu, la production continue d’un système à travers les phases successives du cycle de vie à l’aide d’un ensemble commun de notations et d’outils. Le langage, en particulier, est aussi utile pour l’analyse et la conception que pour la programmation au sens strict du terme. Les outils fournissent des descriptions graphiques des structures système et permettent aux développeurs de produire du texte logiciel à partir des graphiques et de rétroconcevoir les graphiques à partir du texte, en basculant à leur guise entre les deux modes. Cela signifie que les développeurs Eiffel n’ont généralement pas besoin d’un outil CASE distinct (par exemple basé sur UML) pour l’analyse et la conception, mais utilisent plutôt une infrastructure cohérente tout au long du processus.

Cette approche transparente prend également en charge la réversibilité : si une modification est apportée au programme, elle est automatiquement incluse dans les vues d’analyse et de conception. Étant donné que ces vues, comme d’autres vues graphiques et textuelles à différents niveaux de détail, sont extraites du texte logiciel par des outils automatiques, les différents documents associés à un projet sont garantis pour être cohérents. Cela découle du principe du modèle unique, l’un des principes de l’approche Eiffel.

Langage

En tant que langage, Eiffel est un langage orienté objet « pur » (sans doute l’application la plus systématique des principes orientés objet dans les langages existants) basé sur un petit nombre de concepts puissants :

  • Classes, servant de base unique à la fois pour la structure de module et le système de type.
  • Héritage pour la classification, le sous-typage et la réutilisation.
  • Approche prudente et efficace de l’héritage multiple (renommage, sélection, redéfinition, non-définition, héritage répété).
  • Contrats pour écrire des logiciels corrects et robustes, les déboguer et les documenter automatiquement.
  • Gestion disciplinée des exceptions pour récupérer normalement des cas anormaux.
  • Typage statique pour la fiabilité et la clarté.
  • Liaison dynamique pour la flexibilité et la sécurité.
  • Générique, contrainte et sans contrainte, pour décrire des structures de conteneur flexibles. Vous pouvez déclarer une classe VECTOR [G] pour indiquer qu’elle décrit les vecteurs d’éléments de tout type, G désignant un « paramètre générique formel ». Pour dériver un type utilisable, vous fournissez un « paramètre générique réel » comme dans VECTOR [INTEGER] (décrivant les vecteurs d’entiers) ou même VECTOR [VECTOR [INTEGER]] (vecteurs de vecteurs d’entiers).
  • Covariance, permettant l’adaptation flexible des routines lorsqu’elles sont redéfinies dans les descendants de la classe où elles sont apparues à l’origine.
  • Agents : objets fonctionnels de haut niveau décrivant des routines partiellement liées, fournissant la puissance des langages fonctionnels dans un contexte orienté objet et un type sécurisé.
  • Architecture ouverte offrant un accès facile aux logiciels écrits dans d’autres langages tels que C, C++, etc.

Pour une saveur de la syntaxe du langage clair et simple, et du style Eiffel typique (pas encore dans cette première version incluant les contrats), voici le plan d’une classe simple COUNTER décrivant un compteur :

indexing
   description: "[
                  Counters that you can increment by one,
                  decrement, and reset
                  ]"
class
   COUNTER

feature -- Access   

item: INTEGER
         -- Counter's value.

feature -- Element change

increment is
         -- Increase counter by one.
      do
         item := item + 1
      end

decrement is
          -- Decrease counter by one.
      do
         item := item - 1
      end 

reset is
         -- Reset counter to zero.
      do
         item := 0
      end

end -- class COUNTER

Au moment de l’exécution, cette classe aura des instances ; chaque instance est un objet qui représente un compteur distinct. Pour créer un compteur, vous déclarez l’entité correspondante, par exemple :

my_counter: COUNTER

Créez l’objet correspondant :

createmy_counter

(Lorsque le mot clé crée introduit l’opération de création d’objet), puis peut lui appliquer les opérations de la classe (ses fonctionnalités) :

my_counter.increment
…my_counter.decrement
…
print (my_counter.item)

Ces opérations apparaissent dans les fonctionnalités d’autres classes, appelées clients de la classe COUNTER. Quelques commentaires supplémentaires sur cet exemple : Toutes les valeurs sont initialisées par défaut, de sorte que chaque objet de compteur commence sa vie avec sa valeur, élément, initialisée à zéro (vous n’avez pas besoin d’appeler la réinitialisation initialement). En outre, l’élément est un attribut, qui est exporté en mode lecture seule : les clients peuvent, par exemple, imprimer (my_counter.item), mais pas, par exemple, my_counter.item := 657, ce qui constituerait une violation du principe de masquage des informations. Bien entendu, l’auteur de la classe peut décider de fournir une telle fonctionnalité en ajoutant une fonctionnalité :

set (some_value: INTEGER) is
      -- Set value of counter to some_value.
   doitem := some_valueend 

Dans ce cas, les clients utilisent my_counter.set (657)simplement . Mais c’est la décision des auteurs de la classe COUNTER, c’est-à-dire la quantité de fonctionnalités qu’ils fournissent à leurs clients. La clause d’indexation au début de la classe n’affecte pas sa sémantique (c’est-à-dire les propriétés des objets d’exécution correspondants), mais elle joint une documentation supplémentaire à la classe .

Conception par contrat

Eiffel est unique dans les méthodologies et les langages de conception en ce qu’elle applique directement Design by Contract par le biais de constructions telles que les invariants de classe, les conditions préalables et les post-conditions. Supposons par exemple que nous voulons que nos compteurs soient toujours non négatifs. La classe a maintenant un invariant :

indexing
…class
   COUNTER
feature
   …invariant
   item >= 0
end

Etla décrémentation des fonctionnalités a maintenant besoin d’une condition préalable, pour s’assurer que les clients ne tentent pas d’opérations illégales. La mot clé exiger introduit la condition préalable :

decrement is
      -- Decrease counter by one.
   requireitem > 0doitem := item - 1ensureitem = olditem - 1end

Le mot clé assurer l’introduction de la post-condition.

La condition préalable indique au client : « Ne pensez même pas à m’appeler, sauf si vous êtes sûr que le compteur est strictement positif. "

La postcondition dit: « Si vous êtes bon (c’est-à-dire, observez la condition préalable) voici ce que je vous promets de faire pour vous en retour: je diminuerai le compteur d’un."

L’invariant ajoute la promesse: « En outre, toutes mes opérations maintiendront le compteur positif ou zéro. " Les conditions préalables, les post-conditions et les invariants sont appelés assertions.

Bibliothèques

Eiffel met l’accent sur la réutilisation à toutes les étapes, et est pris en charge par un ensemble riche de bibliothèques, soigneusement conçues avec des directives strictes de conception et de style. Il convient de noter ici EiffelBase, qui couvre l’ensemble des structures fondamentales de la science informatique, et EiffelVision, une bibliothèque graphique avancée fournissant des solutions graphiques portables sur de nombreuses plateformes, qui offre aux utilisateurs la garantie de la compatibilité au niveau source et de l’adaptation automatique à l’apparence de la plateforme cible.

Défis

Cette brève introduction à Eiffel est suffisante pour suggérer certains des problèmes qui se sont posés dans l’intégration de .NET Framework. Le modèle objet .NET Framework ne fournit pas de prise en charge native de plusieurs héritages (une classe dans le modèle .NET Framework peut hériter d’au plus une autre classe), pour la générique, pour la covariance ou pour les agents.

Plusieurs de ces mécanismes, en particulier l’héritage multiple, se sont avérés difficiles à implémenter dans le .NET Framework. Elles ont maintenant toutes été traitées avec succès, de sorte qu’il n’y a aucune différence dans la langue prise en charge sous Eiffel pour .NET et d’autres implémentations.

Fonctionnement d’Eiffel dans le .NET Framework

Le ciblage du .NET Framework pour un compilateur de langage signifie vraiment pouvoir produire l’il et les métadonnées associées.

Objectifs

La génération d’il serait suffisant si l’objectif était simplement de compiler Eiffel sous le .NET Framework, mais serait en deçà de notre objectif de fournir une infrastructure à usage général pour l’interopérabilité multilingue, car d’autres langages ne seraient pas en mesure de réutiliser les types Eiffel sans les métadonnées qui les décrivent. L’héritage multiple fournit un exemple typique : la production d’une version IL d’une structure d’héritage multiple, comme indiqué ci-dessous, n’est en soi qu’un problème de compilation et n’est pas nécessairement plus difficile que l’implémentation d’un héritage multiple pour un autre code cible. Le problème plus ambitieux consiste à s’assurer que le code utilisant ces classes dans un autre langage peut voir la hiérarchie d’héritage et en tirer parti, par exemple, en déclarant une variable de type A et en lui affectant une valeur de type D (comme le code Eiffel peut le faire par polymorphisme).

Figure 1. Héritage multiple à partir de classes

L’un des objectifs définis par ISE concernant l’intégration d’Eiffel était la possibilité de réutiliser les types existants écrits dans n’importe quel langage, ainsi que la génération de types qui peuvent être compris par n’importe quel autre environnement de développement .NET Framework. Eiffel est un extendeur .NET Framework, ce qui signifie que vous pouvez écrire des classes Eiffel qui héritent de classes écrites dans d’autres langages, les étendre, puis les recompiler vers IL, ce qui donne à d’autres environnements la possibilité de réutiliser le nouveau type.

Un autre objectif fondamental, en faisant d’Eiffel un acteur à part entière dans les jeux d’interopérabilité .NET, était de fournir ISE Eiffel sous Microsoft® Visual Studio® .NET. Par conséquent, les utilisateurs d’Eiffel ont le choix entre deux modes de développement :

  • Pour un environnement entièrement dédié à Eiffel, ils peuvent utiliser l’environnement EiffelStudio.
  • Pour le développement multilingue et l’intégration étroite avec d’autres langages, par exemple, le débogage multilingue, ils peuvent utiliser Eiffel sous Visual Studio .NET.

Un objectif de conception associé était d’éviter de forcer les utilisateurs à choisir entre ces deux solutions. Il doit être possible de compiler un projet donné alternativement dans EiffelStudio ou Visual Studio .NET.

Enfin, il a été jugé essentiel de permettre l’écriture d’applications ASP.NET et de services web XML dans Eiffel, en incorporant Eiffel dans ASP.NET pages par le biais de la @language="Eiffel" directive.

Propriétés de l’implémentation

Donner à Eiffel la status d’un joueur complet dans les jeux d’interopérabilité .NET a signifié atteindre les propriétés suivantes de l’implémentation d’Eiffel pour .NET :

  • Eiffel est, à compter de la version 5.2, entièrement intégrée à Visual Studio .NET, tirant parti du mécanisme de l’environnement pour l’édition, la compilation, la navigation multilingue et (particulièrement important dans la pratique) le débogage inter-langage. Les « solutions » Visual Studio ont exactement les mêmes status que celles dans d’autres langages et peuvent intégrer (ou être intégrées) des solutions dans d’autres langages.
  • Eiffel pour .NET génère du code managé. Le code généré s’exécute sous le contrôle du CLR .NET Framework, suit ses contraintes et tire parti de ses mécanismes pour la gestion de la mémoire, le garbage collection, la gestion des exceptions, la sécurité, le débogage, etc. Avec. Le logiciel connecté à NET, ISE Eiffel utilise un système d’exécution qui résout des problèmes similaires ; sur le .NET Framework, ses fonctions sont prises en charge par le CLR.
  • Eiffel pour .NET génère du code vérifiable ; vous pouvez produire du code qui répond aux exigences de sécurité .NET.
  • Eiffel pour .NET génère du code conforme CLS. Le code généré répond aux exigences de la Spécification du langage commun, un ensemble de règles qui fait désormais partie de la norme internationale pour le .NET Framework. Cela garantit que les modules produits à partir d’un langage peuvent être réutilisés par d’autres, et fait d’Eiffel un outil idéal pour produire des composants .NET Framework réutilisables de haute qualité sur lesquels toute autre application .NET Framework (écrite, par exemple, en C# ou Microsoft Visual Basic® .NET) peut librement s’appuyer.
  • Eiffel pour .NET est également CLS-consumer et CLS-extender. Cela signifie que les classes Eiffel peuvent utiliser du code conforme CLS à partir d’autres langages, et même hériter d’une classe conforme CLS dans l’un de ces langages et ajouter ou redéfinir des fonctionnalités.
  • Eiffel pour .NET est compatible avec les mécanismes CodeDom, ce qui garantit la traduction possible dans d’autres langages CodeDom et la facilité d’utilisation en tant que langue source dans ASP.NET pour les applications web et les services Web XML.
  • Que ce soit dans Visual Studio .NET ou indépendamment de celui-ci, Eiffel pour .NET est compatible avec les mécanismes de débogage et d’exception du .NET Framework. Une erreur d’exécution déclenchée et non traitée dans un module non Eiffel sera gérée par son appelant Eiffel, et inversement.
  • Conséquence particulièrement significative, les violations de contrat détectées côté Eiffel (si la surveillance des contrats est activée) seront passées en tant qu’exceptions aux appelants non-Eiffel. Cela fournit aux applications une technique inestimable pour détecter les erreurs et améliorer leur fiabilité en tirant parti des installations design by contract d’Eiffel.

Configuration pratique : EiffelStudio

Sous EiffelStudio, la compilation pour le .NET Framework signifie simplement vérifier l’option appropriée dans les paramètres du projet. Par conséquent, quelques boutons supplémentaires s’affichent dans l’interface, y compris le bouton du gestionnaire d’assemblys décrit dans la section suivante.

Le résultat de cette configuration est que les programmeurs Eiffel existants pourront travailler exactement de la même façon qu’avant l’intégration, tout en ayant accès à tous les mécanismes du .NET Framework.

Configuration pratique : Visual Studio .NET

Sous Visual Studio .NET, vous pouvez inclure une solution Eiffel dans n’importe quel projet. Le projet peut inclure des éléments dans Eiffel et des éléments dans d’autres langages, comme dans cette démonstration Microsoft impliquant C# et Visual Basic .NET, ainsi que Eiffel :

Figure 2 : Eiffel et d’autres langages sous Visual Studio .NET

Voici maintenant Visual Studio .NET ouvert sur un projet Eiffel :

Figure 3. Projet Eiffel sous Visual Studio .NET

Le volet gauche affiche un texte de classe (ROOT_CLASS). Le volet droit affiche la hiérarchie des clusters de projet. Notez qu’il utilise les mêmes conventions graphiques pour les classes et les clusters (standard pour Eiffel) que dans EiffelStudio.

Voici un exemple d’utilisation de l’explorateur d’objets Visual Studio .NET (en fait, un navigateur de classes) pour afficher la structure d’héritage et d’autres relations entre classes d’un projet :

Figure 4. Navigateur d’objets (classe) sous Visual Studio .NET

Voici une feuille de propriétés Visual Studio .NET permettant d’afficher et de modifier les propriétés du projet, qui, dans EiffelStudio, apparaît sous la forme « Paramètres du projet » reflétant le contenu du fichier Ace du système :

Figure 5. Feuille de propriétés sous Visual Studio .NET

Notre dernier exemple montre une session de débogage sous Visual Studio .NET pour Eiffel :

Figure 6. Débogage d’un système Eiffel avec le débogueur Visual Studio .NET

Le volet supérieur gauche affiche l’emplacement dans un texte de routine où l’exécution est actuellement arrêtée, ainsi que le texte de classe englobant, avec le mécanisme de navigation dans le volet supérieur droit. En bas à droite se trouve la sortie de l’exécution. Le volet inférieur gauche affiche les variables locales à différents niveaux, leurs types déclarés (deuxième colonne) et leurs valeurs. Vous pouvez utiliser ce volet inférieur gauche, à l’aide des mécanismes Visual Studio .NET, pour définir des « listes watch » et évaluer des expressions à la volée.

Ces exemples montrent que les utilisateurs de Visual Studio .NET pourront tirer pleinement parti des mécanismes Eiffel, et que les utilisateurs Eiffel bénéficieront pour leur part pleinement de Visual Studio .NET.

Tirer parti des mécanismes .NET Framework dans Eiffel

Nous avons vu comment utiliser Eiffel pour créer des composants .NET. Étant donné que le compilateur génère toutes les métadonnées nécessaires, d’autres langages peuvent réutiliser les composants Eiffel comme ils le souhaitent (via l’héritage ou la relation client). La question suivante est « Comment faire réutiliser les composants .NET Framework existants dans Eiffel ? » Fournir un mécanisme complet et facile à utiliser à cet effet est un élément clé de l’offre Eiffel sur le .NET Framework, qui offre la promesse d’une interopérabilité multilingue. Les composants existants peuvent faire partie d’un système écrit dans un autre langage ou, plus important encore, il peut s’agir de composants de bibliothèque, tels que les bibliothèques .NET Framework fondamentales fournies par Microsoft qui sont l’une des principales attractions du framework.

Stratégie

Pour les composants réutilisables, l’objectif est clair : permettre aux développeurs Eiffel de combiner la puissance des bibliothèques Eiffel et des bibliothèques .NET Framework non-Eiffel. Le résultat est une collection sans précédent d’installations de réutilisation.

Côté Eiffel, les bibliothèques telles que EiffelBase (pour les structures de données et algorithmes fondamentaux) et EiffelVision 2 (pour les graphiques portables) sont le résultat d’une décennie et demie de travail continu, et ont atteint un haut niveau de qualité et de praticité.

Côté .NET Framework, un ensemble complet de mécanismes avancés est fourni en particulier par :

  • Bibliothèques de classes de base, fournissant des types de base, des collections, des services de communication à distance, des services de thread, la sécurité, l’accès d’E/S et de nombreuses autres fonctionnalités.
  • Windows Forms pour créer des applications clientes intelligentes.
  • ASP.NET pour la création d’applications web, avec des types tels que DataGrid et HTMLImage.
  • ADO.NET pour la programmation de base de données relationnelle objet.

Eiffel pour .NET permet d’accéder aux deux côtés.

Bibliothèques Eiffel

Grâce à la disponibilité d’Eiffel complète et à la réutilisation des mécanismes .NET Framework, les bibliothèques Eiffel de base sont disponibles pour les développeurs .NET Framework, ce qui offre un avantage pratique considérable. L’une des parties les plus intéressantes de l’offre est EiffelVision 2, une bibliothèque graphique avancée qui fournit un modèle de programmation d’interface graphique graphique élégant et la possibilité de porter une application, graphiques inclus, sur de nombreuses autres plateformes sans aucune modification du code source et avec une adaptation automatique à l’apparence native de la plateforme cible.

La possibilité de combiner des mécanismes Eiffel, comme EiffelVision, avec des mécanismes .NET Framework, tels que Windows Forms, est particulièrement intéressante. Par exemple, vous pouvez incorporer, dans une application EiffelVision éventuellement complexe, un contrôle Windows Form avancé tel qu’un datagrid fournissant un affichage direct d’une base de données via ADO.NET. La figure ci-dessous montre un tel datagrid affiché dans le cadre d’une fenêtre EiffelVision.

Figure 7. Datagrid utilisé avec EiffelVision

Bibliothèques .NET Framework

Du côté du .NET Framework, Eiffel pour .NET par défaut rend les bibliothèques de classes de base pour Windows Forms et ASP.NET directement disponibles pour le développeur Eiffel comme s’il s’agissait de classes Eiffel. Cela signifie que les outils de l’environnement affichent les propriétés de l’interface dans un style cohérent avec ceux d’Eiffel, et que les classes peuvent être utilisées directement, sans avoir besoin de code d’interface spécial.

Cela permet de créer des applications puissantes qui combinent étroitement les avantages de ces bibliothèques avec ceux d’Eiffel, comme illustré dans la figure 7 par la combinaison d’EiffelVision, Windows Forms et ADO.NET.

Le Gestionnaire d’assemblys

Les trois bibliothèques .NET mentionnées ne sont que des exemples (les plus couramment nécessaires) de logiciels non-Eiffel qu’Eiffel pour .NET met à la disposition de n’importe quelle classe Eiffel. Le mécanisme général de mise à disposition de n’importe quel ensemble de classes .NET Framework de cette façon est un outil ISE Eiffel appelé Assembly Manager.

Le principe du Gestionnaire d’assemblys est simple. Vous pouvez appeler le Gestionnaire d’assemblys à partir d’EiffelStudio (l’environnement spécifique à Eiffel) ou de Visual Studio .NET. Vous sélectionnez l’assembly à importer ; en règle générale, il inclut des cours écrits à l’origine dans une langue autre que Eiffel, bien que vous n’ayez pas besoin de connaître cette langue, et il pourrait en fait s’agir d’Eiffel. Le Gestionnaire d’assemblys vous permet de choisir l’assembly parmi ceux du global assembly cache .NET Framework, qui contient des assemblys partagés mis à la disposition de toutes les applications sur l’ordinateur, ou vous pouvez utiliser Parcourir pour rechercher un assembly privé. Une fois que vous avez effectué cette sélection, le Gestionnaire d’assembly génère un ensemble de fichiers XML contenant toutes les informations nécessaires sur les classes de l’assembly. Cela est rendu possible par les mécanismes de réflexion basés sur les métadonnées du .NET Framework. Pour les assemblys globaux, le résultat est stocké dans un Assembly Cache Eiffel, y compris suffisamment d’informations pour permettre aux classes Eiffel d’accéder aux classes de l’assembly comme s’il s’agissait de classes Eiffel, que ce soit à partir d’EiffelStudio (l’environnement spécifique à Eiffel) ou à partir de Visual Studio .NET.

L’une des tâches du Gestionnaire d’assemblys consiste à supprimer la surcharge. Pour des raisons de clarté, de simplicité et de compatibilité avec les principes orientés objet, Eiffel maintient une correspondance un-à-un, au sein d’une classe, entre les noms de caractéristiques et les caractéristiques (pour une raison d’être, voir Meyer 2001). Toutefois, le modèle .NET Framework permet de surcharger un nom avec plusieurs significations. Le Gestionnaire d’assemblys supprime toute ambiguïté en générant des noms uniques pour toute variante de fonctionnalité surchargée. L’algorithme de levée d’ambiguïté sera présenté ci-dessous.

Production de systèmes .NET Framework à partir d’Eiffel

La discussion précédente a décrit les objectifs que nous nous sommes fixés au début du projet en 1999 et qui ont maintenant été atteints par la mise en œuvre actuelle. Nous allons maintenant donner au lecteur une vue des éléments internes, pour expliquer comment nous avons atteint ces objectifs.

Stratégie d’implémentation

Au début du projet, ISE a organisé l’intégration autour de trois jalons majeurs. La première étape de l’intégration a été d’obtenir une première version utilisable tout en évitant les aspects linguistiques les plus délicats, en particulier l’héritage multiple. Cela a donné lieu à un sous-ensemble de langage, Eiffel#, dont l’implémentation est devenue disponible sous forme bêta à la mi-2000 et dans le cadre de la première version publiée d’Eiffel pour .NET en juillet 2001 (version 5.0). Eiffel# incluait la prise en charge de la conception par contrat et de la générique et était suffisant pour créer des applications réelles, mais bien sûr, ce n’était pas la vraie chose. Cela nous a toutefois permis de fournir un produit précoce, d’acquérir une expérience approfondie des problèmes d’implémentation et d’obtenir des commentaires précieux de nos clients.

La deuxième étape a été l’implémentation d’Eiffel complète, y compris l’héritage multiple, les agents et la covariance. Cela faisait partie de la version 5.1 (décembre 2001).

La dernière étape consiste à fournir l’intégration complète de Visual Studio .NET et ASP.NET. Cela fait partie de la dernière version, 5.2, disponible dans le commerce en mai 2002.

Héritage multiple

Comme indiqué, l’héritage multiple a été reconnu dès le début comme un problème d’implémentation clé. Le lecteur peut en effet se demander comment nous pouvons fournir un héritage multiple sur une plateforme qui ne le prend pas en charge, en particulier avec l’exigence énoncée ci-dessus que d’autres langues doivent voir la structure d’héritage multiple Eiffel.

La solution utilisée s’appuie sur la capacité d’une classe .NET Framework à hériter de la multiplication des interfaces, des classes complètement abstraites sans aucune implémentation. Dans le code généré, le compilateur met en ombre chaque classe avec l’interface . La figure suivante montre le résultat de la structure illustrée ci-dessus. Notez que l’équivalent d’une classe A Eiffel d’origine est une classe .NET Framework appelée Impl.A, tandis que l’interface « ombre » conserve le nom de la classe , A, car c’est ce que les programmeurs d’autres langages doivent utiliser :

Figure 8. Ombrage de classes avec interfaces

Les programmeurs qui utilisent ces classes d’un autre langage .NET Framework, tel que C# ou Visual Basic .NET, n’ont pas besoin de connaître les classes Impl . Ils déclarent les variables correspondantes à l’aide de types A, B, C, D. Pour créer des objets des types correspondants, ils utilisent un troisième ensemble de classes générées : Create.A, Create.B , etc. En effet, les interfaces, telles que A, ne peuvent pas avoir de constructeurs (procédures de création dans Eiffel), de sorte que les classes Create fournissent les mécanismes nécessaires, un pour chaque procédure de création de la classe Eiffel correspondante. En utilisant les espaces de noms Impl et Create, cette technique tire pleinement parti des concepts .NET.

Packaging des applications

Le .NET Framework empaque les applications d’une manière originale, à l’aide d’assemblys et de modules plutôt que de fichiers EXE ou DLL simples (exécutables ou bibliothèques de liens dynamiques). Un assembly est constitué d’un groupe de modules et correspond à une application. Un module peut être une DLL ou un EXE. Pour cette raison, le compilateur Eiffel génère un assembly dont le nom est le nom du système comme indiqué dans le fichier de description du système, ou Ace (Assembly of Classes pour Eiffel, écrit dans un langage de contrôle appelé Lace). Vous pouvez spécifier si l’assembly doit être un EXE ou une DLL dans l’option msil_generation_type par défaut comme suit dans le fichier Ace :

systemsample
   root
      ROOT_CLASS: "make"
   default
      msil_generation (yes)
      msil_generation_type ("exe") -- "dll" to generate a DLL
   …

Dans cet exemple, le compilateur génère un seul fichier « sample.exe » contenant l’assembly et le module.

Une autre fonctionnalité spécifique au .NET Framework est la notion d’espace de noms. Tout type .NET Framework est associé à un espace de noms qui garantit l’unicité du nom de type dans le système. Vous pouvez définir un espace de noms par défaut pour toutes les classes du système Eiffel à l’aide de l’option ACE par défaut suivante :

systemsample
   root
      ROOT_CLASS: "make"
   default
      msil_generation (yes)
      msil_generation_type ("exe") -- "dll" to generate a DLL
      namespace ("MyApp")
   …

Dans cet exemple, toutes les classes du système Eiffel sont générées dans l’espace de noms MyApp.<>cluster_name<cluster_name> est le nom du cluster qui contient la classe . Vous pouvez remplacer l’espace de noms par défaut pour chaque cluster comme suit :

systemsample
   root
      ROOT_CLASS: "make"
   default
      msil_generation (yes)
      msil_generation_type ("exe") -- "dll" to generate a DLL
      namespace ("MyApp")
   cluster
      root_cluster: "c:\my_app"
         default
            namespace ("Root")
         end
   …

Avec ce fichier ACE, toutes les classes du cluster root_cluster sont générées dans l’espace de noms Racine. Notez que le nom spécifié dans la clause de cluster n’est pas ajouté à l’espace de noms défini dans la clause par défaut.

Lever l’ambiguïté des noms surchargés

Nous avons noté ci-dessus que les noms de caractéristiques de classes non-Eiffel peuvent nécessiter une levée d’ambiguïté s’ils ont été surchargés.

De manière informelle, l’algorithme ajoute autant de noms de type de signature que nécessaire après le nom de la fonction pour obtenir un nom unique. Par exemple, la fonction C# suivante :

public staticvoidWriteLine (Stringformat, Objectarg0);
   public staticvoidWriteLine (intvalue);
   public staticvoidWriteLine (Stringvalue);

est traduit en Eiffel comme suit :

write_line_string (format: SYSTEM_STRING; arg0: SYSTEM_OBJECT)
   write_line_int_32 (value: INTEGER)
   write_line (value: SYSTEM_STRING)

Par défaut, les noms de type utilisés par l’algorithme n’incluent pas les espaces de noms. Dans de rares cas de conflit entre des noms de type dans différents espaces de noms, des chiffres sont ajoutés pour supprimer toute ambiguïté restante.

Notez que cet algorithme s’applique uniquement aux fonctionnalités surchargées. Les noms non surchargés restent intacts, à l’exception d’une adaptation facultative à différentes conventions de casse des lettres. (Les règles de style Eiffel prescrivent clairement la séparation des mots successifs dans un nom de caractéristique par des traits de soulignement, comme dans write_line. Cela diffère de la convention CamelCase généralement appliquée par C# et d’autres langages .NET. Les utilisateurs peuvent choisir de conserver les noms d’origine ou de les convertir en la convention Eiffel familière.)

Types de base

Le Gestionnaire d’assemblys doit également s’occuper du mappage des types CLS dans leurs équivalents Eiffel, pour garantir la sémantique correcte. Le tableau suivant présente la correspondance :

Type primitif CLS (description) Eiffel Type
System.Char (entier non signé de 2 octets) CHARACTER
System.Byte (entier non signé de 1 octet) UNSIGNED_INTEGER_8
System.Int16 (entier signé de 2 octets) INTEGER_16
System.Int32 (entier signé de 4 octets) INTEGER
System.Int64 (entier signé de 8 octets) INTEGER_64
System.Single (nombre à virgule flottante de 4 octets) real
System.Double (nombre à virgule flottante de 8 octets) DOUBLE
System.String (chaîne, zéro ou plusieurs caractères, null autorisé) SYSTEM_STRING
System.Object (racine de toutes les hiérarchies d’héritage de classe) SYSTEM_OBJECT
System.Boolean (true ou false) BOOLEAN

Classes externes

Eiffel a longtemps fourni une syntaxe étendue pour accéder aux mécanismes à partir d’autres langages, en particulier C, C++ et Java. Non seulement vous pouvez appeler des routines écrites dans ces langues, mais vous pouvez également les laisser rappeler dans un système Eiffel, via la bibliothèque Cecil . Vous pouvez également spécifier qu’une routine Eiffel est mappée à une macro C, que vous souhaitez utiliser un certain constructeur ou destructeur C++ pour une classe particulière, qu’une routine C ou C++ a une signature de type particulière dans son langage d’origine, qu’une paire de routines getter-setter manipule directement un certain champ d’un struct C, et ainsi de suite. Cela a permis d’utiliser Eiffel en tant que combinatoire de composants, un outil largement ouvert sur le monde extérieur qui permet aux développeurs système de tirer parti des mécanismes architecturaux d’Eiffel (classes, héritage unique et multiple, génériques, conception par contrat, masquage d’informations, accès uniforme) aux composants de package qui peuvent provenir de différents langages.

Quelques extensions ont été apportées à ce mécanisme externe pour tenir compte des fonctionnalités spécifiques de .NET :

Type de fonction .NET Eiffel Externe
Méthode « Signature IL ... utiliser class_name »
Méthode statique « Signature IL ... utilisation statique class_name »
Getter de champ « Signature IL ... field use class_name »
Getter de champ statique « Signature IL ... static_field utiliser class_name »
Field Setter « Signature IL ... set_field utiliser class_name »
Setter de champs statiques « Signature IL ... set_static_field utiliser class_name »
Constructeur « Signature IL ... creator use class_name »

Les fonctionnalités externes peuvent être appelées à partir de clients ou de descendants de la classe comme vous appelez n’importe quelle autre fonctionnalité Eiffel. Par conséquent, si votre système inclut une fonctionnalité qui a besoin d’une entrée utilisateur, il peut inclure le code suivant :

need_user_input is
         -- Take user input and do stuff.
      localio: SYSTEM_CONSOLEinput: STRINGdocreate io.makeinput := io.read_line -- calls System.Console.ReadLine()
         -- do stuff
      end

Dans ce cas, read_line est un externe statique, vous n’avez donc pas besoin d’instancier io. Au lieu de cela, vous pouvez utiliser la syntaxe feature {CLASS}.static_routine:

need_user_input is
         -- Take user input and do stuff.
      localio: SYSTEM_CONSOLEinput: STRINGdo
         -- removed creation of io
         input := feature {SYSTEM_CONSOLE}.read_line
         -- do stuff
      end

Conclusion

Eiffel pour .NET combine deux des technologies logicielles les plus intéressantes à avoir vu le jour au cours d’une décennie. La puissance combinée de la plateforme et de l’environnement de développement devrait produire l’environnement de rêve pour la création d’applications puissantes que la société attend de nous aujourd’hui.

Le degré d’intégration permet aux développeurs d’utiliser les fonctionnalités les plus avancées des deux technologies. La flexibilité de l’ensemble d’outils( prise en charge à la fois d’EiffelStudio sur .NET, pour les développeurs issus d’un environnement Eiffel, et Eiffel pour Visual Studio .NET, pour une intégration étroite avec d’autres langages .NET Framework et l’utilisation d’outils courants pour la modification, la compilation, la navigation et le débogage) permet à chaque entreprise d’adopter le modèle de développement qui correspond le mieux à ses besoins et à sa culture.

Eiffel sur .NET offre flexibilité, productivité et fiabilité élevée. Il est en particulier impossible de surestimer les avantages de Design by Contract dans un environnement distribué, où la recherche de bogues après coup peut être une expérience atroce et coûteuse.

Le niveau de réutilisation fourni par la combinaison des bibliothèques Eiffel et .NET offre un avantage concurrentiel immédiat et exceptionnel, ce qui permet aux entreprises de tirer parti de solutions réutilisables qui sont le produit de milliers d’années d’efforts axés sur la qualité pour se concentrer sur la mise sur le marché de leurs propres produits à valeur ajoutée rapidement et avec succès.

Avec les autres avantages de la méthode Eiffel (développement fluide, programmation générique, masquage d’informations, mécanisme d’héritage puissant et autres principes d’ingénierie logicielle), Eiffel sur .NET fournit une solution de pointe pour les développeurs de logiciels Internet ambitieux.

Références

[ISE] Eiffel Sites web à http://www.eiffel.com/ et http://www.dotnetexperts.com/.

[Meyer 1992] Bertrand Meyer: Eiffel: The Language, Prentice Hall, 1992.

[Meyer 1997] Bertrand Meyer: Object-Oriented Software Construction, 2e édition, Prentice Hall, 1997.

[Meyer 2001] Bertrand Meyer: Overloading vs. Object Technology, in Journal of Object-Oriented Programming (JOOP), novembre 2001.

À propos des auteurs

Raphael Simon est ingénieur senior chez Interactive Software Engineering (Santa Barbara, Californie), en charge de la division applications et outils Windows.

Emmanuel Stapf est ingénieur senior chez ISE, responsable de la division compilateur et environnement.

Bertrand Meyer est professeur de génie logiciel à l’École polytechnique fédérale (EPF) de Zurich et conseiller scientifique de l’ISE, ainsi que professeur associé à l’université Monash de Melbourne (Australie).

Pour contacter les auteurs, utilisez info@eiffel.com.