POURQUOI CE BLOG, POUR QUI ?


POURQUOI CE BLOG, POUR QUI ?
Ce Blog s'adresse à tous ceux qui sont passionnés par les sciences informatiques , Professionnels,Etudiants,Amateurs ...
Les sujets exposés dans la suite se rapporteront essentiellement sur l'analyse informatique,la programmation,le développement ainsi que à l'architecture IT.
QUI SUIS JE ?
Je suis Kangulungu Lubilanji, Consultant-Freelance sur les technologies .NET,C#,ASP.NET ... Contactez moi pour plus d'informations.

La Programmation OO: Chapitre 15. Interfaces

Chapitre 15. Interfaces
Ce chapitre présente les interfaces, structures de code qui se bornent à introduire les seules signatures des méthodes. Il décrit les trois rôles que les interfaces sont appelées à jouer : forcer l'implémentation de leurs méthodes, permettre le multihéritage, faciliter et stabiliser la décomposition de l'application logicielle.

Interface : favoriser la décomposition et la stabilité, nous avons entr'apercus les interfaces, dans un chapitre précédent, comme une structure de code dont la finalité première est d'extraire d'une classe l'ensemble des signatures de ses services, afin d'en informer toutes celles qui voudraient y faire appel. Ces classes n'ont nul besoin de détails d'implémentation, c'est à dire de la manière précise dont ces services seront réellement exécutés (le corps d'instruction) par l'objet qui les fournit. Il suffira d'appeler le service par son nom pour le voir s'exécuter.

L'utilisation d'interfaces conduit naturellement à des applications facilement décomposables, réparties à travers une large équipe de programmeurs, tout en permettant une  forte résistance aux changements d'implémentation. Ce sont les interfaces qui circuleront de programmeur en programmeur car ce sont les seuls éléments de code dont chacun d'eux, occupé à la réalisation de sa classe, a besoin dans son interaction avec les autres classes. Une fois sa classe bien entamée, ce programmeur en extraira également les services qu'il doit rendre disponible aux autres. C'est l'aboutissement naturel de l'encapsulation, quand elle est pratiquée à l’extrême. On dissimule tout ce qui concerne l’implémentation des classes, au point d'en faire un fichier distinct et inaccessible, au contraire du fichier reprenant les noms des seuls services rendus par cette classe, qui est disponible, lui, pour tout utilisateur.

Interface via l'héritage, en C# des classes abstraites aux interfaces il n'y a qu'un pas, puisqu'une interface est une classe abstraite dont toutes les méthodes sont déclarées abstraites. C# n'autorise aucun attribut dans la définition de ses interfaces, il n'y a pas de différence syntaxique entre l'héritage de classe et d'interface.
class 01 : 02 , I02

à ceci près que les interfaces doivent être héritées en dernier, c'est à dire à la suite de la seule classe dont on peut hériter.

Les interfaces peuvent normalement hériter entre elles, comme le diagramme de classe présenté ci-après l'indique. En UML, le lien continu représente l'héritage et le lien en pointillé représente l'implémentation (C# ne faisant pas de différence entre les deux). Quand ils concernent les interfaces, les graphes d'héritage peuvent être plus complexes que quand ils se limitent aux seules classes. Ces graphes peuvent, en effet, présenter des ramifications multiples, tant descendantes qu'ascendantes (sinon ils se restreindraient à des structures d'arbre).    
Structure d'héritage des interfaces. Dans ce diagramme de classes UML, figurent quatre interfaces 02, 01, 001, et 002 et deux classes, 0001 et l'interface.

Les trois raisons d'être des interfaces :
Forcer la redéfinition, Les interfaces ont principalement trois raisons d'être. La première, largement exploitée est de forcer le programmeur à réutiliser des fonctionnalités déjà prédéfinies dans les librairies d'utilitaires.
Implémentation d'interfaces, en C#  quand une classe implémente ou hérite d'une interface, elle est contrainte et forcée de concrétiser les méthodes qu'elle hérite de celle-ci. Ne pas le faire génère une erreur lors de la compilation. L'interface oblige à utiliser et à redéfinir ses méthodes.
Permettre le multihéritage, plusieurs interfaces seront très souvent implémentées en même temps. De fait , un autre apport des interfaces est d'avoir levé l'interdiction du multihéritage : d'interdit pour les classes, il devient autorisé pour les interfaces. Comme dans le diagramme UML précédent, rien n'interdit deux interfaces de posséder deux signatures de méthodes égales (dans ce diagramme, la même signature de méthode jeTravaillePour001() apparaît deux fois), puisque seule la classe, plus bas dans le graphe d'héritage, fournira un  contenu à ces méthodes. De même, une classe et une interface pourraient partager la même signature de méthode. Si elle se trouvent héritées par une sous-classe. Concernant les attributs, leur disparition pure et simple du C# réduisent également les problèmes posé par des noms égaux.

La carte de visite de l'objet, finalement l'interface peut s'assimiler à la carte de visite d'une classe (et de tous les objets auxquels elle donne naissance), qu'un développeur d'une autre classe consulte et s'engage à respecter dans la conception de la sienne. Le diagramme UML qui suit illustre une collaboration idéale entre deux programmeurs, le premier de la classe 01 et l'autre de la classe 02.

Ce qui apparaît dans ce diagramme, c'est que l'unique dépendance de la classe 01 avec la classe 02 passe par un lien avec la seule interface de la classe 02, c'est dire I02.l'unique fichier dont doit disposer le développeur de la classe 01 est un fichier contenant  le code de l'interface I02, et aucun autre. L'existence d'une implémentation quelconque de cette interface par une classe, ici la classe 02, peut ne le préoccuper en rien. Son contrat de développement, il le signe avec l'interface et non avec la classe.

C'est la responsabilité des autres développeurs de la classe 02, de faire en sorte que ce soit bien l'interface I02, et aucune autre, qui soit implémentée. Les codes C# correspondant à ce diagramme sont présentés ci-après, répartis, dans les deux cas, et comme cela doit l'être idéalement, sur 4 fichiers séparés : les deux classes, les deux interfaces et la classe principale. Le développeur de la classe 01 n'aura à manipuler que trois d'entre eux, au début, I01, pour le donner aux autres puis I02 pour savoir comment s'adresser à la classe 02 et, plus longuement, 01.
Fichier 1 : IO1.cs
   public interface I01{
        void jeTravaillePour01();
    }
Fichier 2 : IO2.cs
    public interface I02
    {
        void jeTravaillePour02();
    }
Fichier 3 : O1.cs
    public class O1 : I01 {
        private I02 un02;

        public O1() { }
        public void set02(I02 un02)
        {
            this.un02 = un02;
        }
        public void jUtilise02() 
        {
            Console.WriteLine("j'utilise 02");
            un02.jeTravaillePour02();
        }

        public void jeTravaillePour01()
        {
            Console.WriteLine("je travaille pour 01");
            JImplementeLeService01();
        }
        private void JImplementeLeService01()
        {
            Console.WriteLine("je suis prive dans 01");
        }
    }
    public class Fils01 : O1, I01
    {
        public new void jeTravaillePour01()
        {
            Console.WriteLine("je travaille pour fils 01");
        }
    }
Fichier 4 : O2.cs
    public class O2 : I02
    {
        private I01 un01, unAutre01;

        public O2(I01 un01, I01 unAutre01) 
        {
            this.un01 = un01;
            this.unAutre01 = unAutre01;
        }
        public void jeTravaillePour02()
        {
            Console.WriteLine("je travaille pour 02");
            JImplementeLeService02();
        }

        public void jUtilise01()
        {
            Console.WriteLine("j'utilise 01");
            un01.jeTravaillePour01();
            unAutre01.jeTravaillePour01();

        }
        private void JImplementeLeService02()
        {
            Console.WriteLine("je suis prive dans 02");
        }
    }
Il  sera toujours important de se préoccuper des problèmes de typage statique et dynamique (et de l'emploi soigné des mot-clés : abstract, virtual, new et override ), si l'héritage se propage vers la bas, et que de nouvelles classes héritent de la classe 01. C'est le cas ici avec la classe Fils01. Comme seule la classe 01 implémente l'interface, si nous forçons  pas la classe Fils01 à ré-implémenter à son tour la même interface, par défaut et à cause du typage statique, le comportement de la méthode jeTravaillePour01() sera celui de la superclasse 01, la première à implémenter l'interface et non pas celui de Fils01. N'oubliez pas que C# n'est pas polymorphique par défaut. Il a fait le choix de ne rien être par défaut et de cous obliger à préciser vos intentions.
C# a rendu cette pratique plus contraignante et moins intuitive, en évitant que vous sous laissiez guider par le seul fonctionnement par défaut. Ainsi, cette ré-implémentation à plusieurs niveaux des interfaces pourrait conduire le programmeur C# à se trouver confronté  à des problèmes de signatures de méthodes partagées entre plusieurs interfaces, qu'il ne pourra trancher qu'en précisant de quelle interface est issue la méthode (par une écriture comme « I01.jetravaillePour01(){....} »).
A lire les développeurs de .Net, il  semble que la raison essentielle de ce choix, ainsi que la présence du new dans la redéfinition des méthodes, soient liées à la possibilités de  faire coexister dans un même code plusieurs versions des mêmes fonctionnalités. Tant que vous n'êtes pas convaincu du développement en cours de la méthode   jetravaillePour01() dans la classe Fils01, n'implémentez pas l'interface I01 dans celle-ci. Par défaut, c'est la version de la classe 01 qui s'exécutera. Dès que vous êtes sûr de vous, vous pouvez ajouter l'implémentation de l'interface I01. C'est automatiquement la nouvelle version qui sera exécutée, bien qu'il suffise d'un simple retrait pour revenir à la version précédente. Ainsi, les deux versions peuvent coexister dans un même code. Cela peut aider le développement à innover, puisqu'il à la certitude qu'une version ancienne continue à fonctionner comme roue de secours.

Ainsi en C#, même les structures peuvent hériter des interfaces et permettre à leur méthode de  « s'extérioriser ». Ces interfaces serviront encore dans la nouvelle plate-forme .Net, en tant que pont entre les différents langages de programmation supportés pas la plate-forme, tels-que VB.Net, C#, C++, Python.Net et JScript. Ainsi, il sera possible d'utiliser du code C#, par l'entremise de son interface .dll, à l'intérieur d'un macro VB.Net. Les interfaces, ici, serviront non seulement à la médiation entre des classes écrites dans des environnements différents, mais élargiront cette médiation à des langages de programmation différents.
Interfaces : du local à Internet
Par fichier séparés, ce qu'il y a de vraiment commun aux trois langages de programmation OO, c'est le besoin de séparer dans deux fichiers différents les signatures des méthodes de leur implémentation, ce qui est visible et accessible de l'extérieur et la manière dont cela s'exécute de l'intérieur.

Nous allons généraliser dans le prochain  chapitre cette interaction entre objets, par le truchement de leur interfaces, à Internet. Les interfaces sont à ce point suffisantes à la communication entre objets que seuls les fichiers qui les contiennent devront être installés sur les ordinateurs séparés, mais appelés à communiquer. La véritable implémentation des services sera non seulement encapsulée dans les fichiers classes, mais également dans des ordinateurs séparés, ordinateurs simplement connectés par Internet et son protocole de communication : TCP/IP.




Aucun commentaire: