La programmation Objet appliquée à .Net par l’exemple Partie 1 sur 3

Navigation globale

Introduction

Le FrameWork .Net a introduit dans un langage Microsoft le principe de programmation (tout) objet.
Visual Basic 1 (puis 2,3…6) avait amorcé la notion de programmation orientée objet.
Il y avait les contrôles fournis, on pouvait créer ses contrôles personnalisés et même des objets métier, sans interface graphique (honnêtement je n’ai pas souvenir d’en avoir vu). Leur intégration dans le code était un mélange de programmation séquentielle et événementielle.
En parallèle, Microsoft a développé sa version de C++ (langage multi paradigme).

L’essor de Java a conduit Microsoft à créer le FrameWork .Net (qui n’est pas vraiment un FrameWork, mais c’est un autre débat) et son langage associé : le C#.
Pour ne pas « perdre » la communauté de développeurs VB autodidactes, le faux ami VB.Net était sensé remplacer VB. Sauf qu’un C# avec un skin VB, quelques souplesses et même une option par défaut qui remet un peu de VB6 n’a pas convaincu grand monde, et depuis quelques 18 ans, de nombreux « VB6istes » font de la « résistance ». Pire ceux qui finissent par tenter le coup, ou les débutants, butent devant la thèse de départ: en .Net tout est objet.

Ce tuto vise à familiarise le débutant en .Net à la programmation objet,

  • avoir acquis les bases de C# ou VB.Net est indispensable,
  • avoir déjà un peu d'expérience en programmation est utile.

Un objet, c’est quoi?

Dans la vie courante, un objet est une chose conçue pour répondre à un besoin précis. Cette chose peut interagir avec d’autres choses et/ou un (des) utilisateur(s).
Une cafetière par exemple, est supportée par un meuble, alimentée par une prise électrique. Elle utilise des consommables : de l’eau, des filtres et du café moulu. Enfin elle est manipulée par quelqu’un. Elle dispose d’un bouton marche/arrêt, d’un autre lançant le cycle, d’un voyant signalant la fin du cycle et d’un autre qu’il n’y a plus d’eau. Et bien sûr, elle produit du café.
En programmation, c’est pareil, un objet est un code développé pour répondre à un besoin précis. Il peut interagir avec d’autres objets et/ou l’utilisateur. Il consomme des données. Il dispose de points d’entrée permettant de déclencher des processus. Il peut alerter les autres objets d’un changement quelconque. Et il produit ce pourquoi il a été écrit.

Un peu de terminologie

Class:

La classe est le code permettant de construire un objet.

Instance:

Un objet créé à partir de la classe.
Ici, l’analogie est très claire.

On crée un plan de construction d'une maison qui réunit les instructions destinées à la construction.
Mais le plan n'est pas une maison.
La maison est un objet qui a été instancié (construit) à partir de la classe(le plan).
A partir du plan (la classe) on peut construire une autre maison (l’objet).
En bref, la classe c'est le modèle, et un objet c'est une instance.

Propriété:

Caractéristique paramétrable d’un objet. Par exemple deux cafetières d’un modèle identique peuvent avoir une couleur différente.

Méthode:

Morceau de code exécutant une action. Elle peut retourner un résultat ou pas, et prendre des paramètres en entrée ou pas.

Surcharge:

Méthode ayant le même nom qu’une méthode existante, exécutant la même action, mais à partir de paramètres différents ou ayant un type de retour différent.

Constructeur:

Méthode particulière, sans type de retour, appelée au moment de l’instanciation de l’objet. Un constructeur peut être surchargé.

Signature:

La signature d’une méthode est le nombre, le type et l’ordre des paramètres qui lui sont fournis.
Si la méthode Multiplication nécessite deux nombres, sa signature est « Deux nombres ».
On distingue les surcharges par leur signature, chaque signature doit être unique.

Factorisation:

Principe de regroupement en un seul code, plusieurs codes quasi identiques.
Par exemple on veut programmer un piano, on pourrait dans le code d’appui du Do de la première octave écrire le code qui va jouer un do à 32,7Hz, puis à la note de droite écrire le code qui joue un Ré à 36,7Hz, etc.… et ce 88 fois.
Si on factorise, on écrit une seule méthode, qui via un paramètre sait quelle touche est jouée, détermine la fréquence et la joue.
Dans le premier cas, si le code fonctionne sur PC, mais pas sur smartphone, il faut le corriger 88 fois, dans le second une seule.

Évènement:

Alerte signalant à d’autres objets un fait particulier. Les objets voulant être avertis doivent s’abonner à l’évènement de l’instance.
C’est un peu comme la liste de diffusion d’une association : en vous y abonnant, vous serez alertés, par mail, des dernières actions, d’une sortie, d’un spectacle, bref d’un évènement.

Délégué:

Variable représentant une méthode : les délégués ont de nombreuses applications. Dans ce tutoriel, nous n’en verrons qu’une. Lors de la création d’un évènement, les délégués servent à définir la signature de la méthode abonnée.

MVC:

Modèle Vue Contrôleur, principe de programmation en « couches » ou une couche s’occupe des données (stockage, extraction, sauvegarde, etc..), une couche métier qui fait « le travail » principal du programme et une couche interface avec l’homme qui permet la saisie de données et affiche les résultats.
Plus d'informations ici.
La programmation objet se prête parfaitement à ce principe. .Net offre deux couches Vue possibles : WinForm et WPF.

L’héritage:

C’est un principe fondamental de la programmation objet.
Plus haut j’ai décrit le fonctionnement d’une cafetière à filtre. Admettons que l’on doive coder une cafetière à filtre et une cafetière à capsule.
Plutôt que d’écrire deux classes avec 90% du code en commun, (l’alimentation électrique, le besoin d’eau, le bouton Marche/Arrêt, le bouton début de cycle, etc…), il est judicieux d’écrire une classe Cafetiere qui contiendra tout le code commun. Ensuite on va faire hériter ce code à la classe CafetiereFiltre. Tout ce qui existe dans Cafetiere, existe aussi dans CafetiereFiltre, il faudra bien sûr ajouter l’approvisionnement en filtre et en café moulu, ainsi que la façon de faire le café.
Enfin on fera de même avec CafetiereCapsule.
C’est donc un moyen de factoriser le code, mais ça ne se limite pas à ça.

Si je dois fournir un consommable à ma cafetière, je dois savoir si c’est une cafetière à filtre, ou à capsule. Par contre pour lancer le cycle, il me suffit de savoir qu’il s’agit d’une cafetière. D’ailleurs pour la brancher, il me suffit de savoir que c’est un appareil électrique (tout comme le four, et le pétrin du boulanger). Donc, quand mon programme va dire « Coupure de courant! », les fours, les pétrins et les cafetières vont s’éteindre. C’est ce qu’on appelle le polymorphisme. Tout objet est du type de la classe qui l’a instancié mais aussi de toutes les classes dont il dérive.
Attention, ça ne marche que dans un sens, une cafetière est un appareil électrique, mais tous les appareils électriques ne sont pas des cafetières.

Plus haut je vous ai dit qu’en .Net tout est objet, et bien cette affirmation est deux fois vraie.
Non seulement tout ce que l’on code est tout ou partie d’un objet, mais en plus tout ce que l’on code dérive, via la chaine d’héritage d’un seul et même objet du type object.

Regardons une instance d’un object
Ces 4 méthodes sont présentent dans toutes les classes du FrameWork et le seront dans toutes vos classes.
ToString() retourne une string représentative de l’objet, par défaut c’est le nom du type. Mais ces 4 méthodes sont prévues pour être réécrites dans les classes filles, et par exemple ToString d’un entier retourne le nombre converti en string.

deux = 2.ToString()
deux vaut"2"

Et oui ça marche même avec un nombre tapé dans le code! A noter tout de même quand on tape le point, la liste déroulante d’Intellisense ne s’affiche pas. En effet le compilateur ne peut pas savoir si on veut écrire un nombre décimal ou une méthode.

Si on essaye avec une string On constate qu’il y a du monde! Mais Equals(), GetHasCode(), GetType() et ToString() sont bien présentes.

Par contre, il n’est possible de dériver que d’une seule classe mère.
Prenons un carré, c’est un quadrilatère avec 4 angles droits, c’est donc un rectangle => le carré dérive du rectangle.
Mais c’est aussi un parallélogramme dont les 4 cotés ont une longueur égale, c’est donc un losange => le carré dérive du losange.
Et bien en .Net on ne pourra pas créer cette double ascendance, il faudra choisir l’une ou l’autre.

Espace de nom:

Ensemble dans lequel sont écrites des classes ayant un lien, notre projet est un espace de nom.
On peut créer une hiérarchie d’espaces de noms.
Quand on utilise des objets du Framework, on doit se référencer à leurs espaces de noms

Modificateur:

Des mots clés permettent de rendre visible ou invisible un membre (propriété, méthode et évènement) pour les autres objets (les mots clés peuvent être différents en C# et VB.Net, pour la suite j’utiliserai donc 2 couleurs pour différencier C# et VB Net.)
-

private

ou

Private

: seule l’instance de l’objet a accès à ce membre (il est privé)

-

public

ou

Public

: ce membre est visible pour tous (il est public).

-

protected

ou

Protected

:ce membre est accessible de la classe et des classes filles, si rien n’est spécifié, le membre est considéré privé (il est protégé).

-

virtual

ou

Overridable

: le membre est prévu pour être substitué dans les classes filles, la méthode ToString par exemple est virtuelle.

-

abstract

ou

MustOverride

: le membre doit être substitué, complété dans les classe filles(il est abstrait).

-

new

ou

Shadows

: le membre masque un membre hérité qui n’avait pas été prévu pour être substitué. En C#, ne pas confondre avec le new servant à l’instanciation.

Une classe peut être publique ou privée, dans ce cas sa visibilité est par rapport à l’espace de nom dans lequel elle est écrite.

Pour empêcher qu’une classe puisse être dérivée, on peut la sceller (

sealed

en C#) ou la rendre non héritable (

NotInheritable

en VB Net). Elle ne pourra donc pas avoir de classes filles

Nous verrons les classes partagées et abstraites plus loin.

Collection:

Une collection est une variable «contenant » des objets du même type.
Il existe plusieurs types de collections, la plus simple à utiliser est la List.
Grace au polymorphisme, on peut créer des collections d’objets qui de prime abord ne sont pas du même type.
Une collection d’appareils électriques pourra contenir des pétrins et des cafetières.
Un objet peut être dans plusieurs collections en même temps, par exemple, un Habitant peut être dans la collection Ville et dans la collection Rue et dans la collection Immeuble et dans la collection Etage et dans la collection Appartement.

Récursive:

Si dit d’une méthode qui s’appelle elle-même. Cela sert à faire des actions répétitives.

Enumération:

C’est un type de données permettant de créer un jeu de constantes avec des noms. Ces constantes représentent des valeurs entières.
Par défaut la première vaut 0, la seconde 1, etc.… mais on peut définir des valeurs personnalisées si besoin. Par exemple l’énumération Polygone serait définie par Triangle valant 3, Quadrilatère, valant 4, Pentagone valant 5 etc.

Et l’exemple dans tout ça?

Je vous propose d’écrire la couche métier d’un célèbre jeu de cartes dont le but est de parcourir mille kilomètres en évitant les embuches dressées par nos adversaires.

Il y a 4 types de cartes:

  • les kilomètres
  • les embûches
  • les parades
  • les bottes

On va donc pouvoir écrire une classe Carte de laquelle dériveront les 4 autres.

La classe Carte:

En soit, cette classe n’a pas d’autre utilité que d’être la mère des 4 autres classes, leur modèle. On n’instanciera pas d’objet Carte. On va donc en faire une classe abstraite, c’est à dire une classe uniquement destinée à être dérivée. Elle ne peut ni être instanciée ni utilisée.

Une carte a comme propriété

  • un dessin, une face (ou le chemin vers un fichier image qui représente cette face)
  • un nombre de points

Elle peut

  • être piochée ou se piocher
  • être jouée ou se jouer
  • être défaussée ou se défausser

Notez que j’ai utilisé la voie passive et transitive en les présentant comme deux alternatives, c’est que soit la carte est manipulée par un autre objet (le joueur, le moteur jeu etc…), soit elle se manipule elle-même. Il sera du ressort du concepteur de choisir entre ces 2 options.
Pour ce tutoriel, nous travaillerons les 2.
Ces actions se font au tour du joueur.
Pour les réaliser la carte aura besoin de connaitre le joueur. Je fais le choix que celui-ci sera passé en paramètre de la méthode Pioche et stocké dans une variable privée.
Je reviendrai plus tard sur la pioche et la défausse.
La méthode Jouer n’aura pas de paramètre et sera protégée : je vais expliquer pourquoi un peu plus bas

Le constructeur permet d’affecter le nombre de points et l’image.

La classe Kilometre:

Dérive de Carte.

Elle a comme propriété spécifique le nombre de kilomètres, qui est égal à son nombre de points.

Elle a une méthode publique Jouer qui appelle celle de Carte.

La classe Embuche:

Dérive de Carte.

Son nombre de points est 0.
Un joueur la joue contre un autre joueur. Le joueur cible sera passé en paramètre de la méthode Jouer.
Il y a 5 types d’embuches

  • Feu rouge
  • Limitation de vitesse
  • Crevaison
  • Panne d’essence
  • Accident

Le constructeur permet d’affecter le type.

Elle a une méthode publique Jouer qui prend le Joueur cible en paramètre. Si la méthode Jouer de Carte était publique, alors Embuche aurait les 2 surcharges, il y aurait donc un risque d’utiliser la mauvaise. Donc dans Carte la méthode Jouer sera privée.

Enumération TypeEmbuche:

Les classes Embuche, Parade et Botte ont une action liée. On définit donc une énumération (sans valeurs spécifiques) décrivant chaque embuche.

La classe Parade:

Elle contrecarre une embuche.
Son nombre de points est 0.

Il y a 5 type de parades, qui parent chacune un type d’embuche

  • Feu vert
  • Fin de limitation de vitesse
  • Roue de secours
  • Pompe à essence
  • Réparations

Elle a une méthode publique Jouer qui appelle celle de Carte

Le constructeur permet d’affecter le type et l’embuche parée.

La classe Botte:

Son nombre de points est 100.
Elle immunise d’une embuche.
Il y a 4 bottes:

  • Le véhicule prioritaire
  • La roue increvable
  • La citerne d’essence
  • L’as du volant.

Elle peut être jouée en réponse à une attaque, dans ce cas dit « coup fourré », elle donne 3 bonus à son possesseur:

  • 300 points supplémentaires
  • Piocher pour remplacer la carte dans la main
  • C’est à son tour de jouer et il n’a besoin ni de parade ni de feu vert

Elle a une méthode publique Jouer qui appelle celle de Carte

Le constructeur permet d’affecter le type de l’embuche immunisée.

On pourrait dériver les embuches, les parades et les bottes, cependant on se retrouverait avec 14 classes supplémentaires. On peut assez facilement gérer le type et qui pare qui avec des propriétés.

Pour chaque classe fille
On va réécrire la méthode ToString de façon à ce qu’elle affiche de quel type de carte il s’agit.

La suite du tutoriel

Ce document intitulé « La programmation Objet appliquée à .Net par l’exemple Partie 1 sur 3 » issu de CodeS SourceS (codes-sources.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.
Rejoignez-nous