Archives Wild Code School
C#

1. Les classes

Quête 6 sur 46 · 2 images archivées

Lorsque tu auras terminé cette quête, tu sauras décrire des comportements d'objets en utilisant des classes

Objectifs

  • Comprendre ce qu'est une classe
  • Maîtriser la syntaxe de création de classe
  • Utiliser des méthodes
  • Utiliser des attributs
  • Instancier des classes

Le concessionnaire

Tu es propriétaire d'une société de vente de voitures. Ta problématique principale est d'acheter puis de revendre des voitures. Tu dois fidéliser tes clients, leur vendre des voitures à un bon prix, rémunérer tes vendeurs et t'assurer que ta société fait des bénéfices.

Problème, l'informatique est restée opaque pour toi trop longtemps, et aujourd'hui tu es fatigué de remplir de la paperasse toute la journée pour t'assurer que ta société est toujours en croissance et que cela va continuer.

Si tu agis comme cela, c'est pour une bonne raison : tu ne voulais pas utiliser quelque chose hors de ton contrôle, hors de ta connaissance, et c'est tout à fait louable.

Lorsque tu calculais tes recettes hier, tu t'es rendu compte qu'il y avait un manque à gagner au niveau de ton temps qu'un logiciel pourrait te supprimer. Comme tu ne comprends pas très bien comment ça marche, mais que tu envisages quand même de l'acheter pour pouvoir partir en vacances sans t'inquiéter de l'avenir de ton entreprise, tu as décidé de te mettre à apprendre ce qu'est un logiciel et comment il est construit.

C'est l'occasion pour toi de maîtriser une nouvelle compétence et d'apporter un nouveau souffle à toi et tes employés.

Un modèle réaliste

Puisque la programmation est très abstraite et suffisamment complexe, nous, les programmeurs, cherchons constamment à simplifier le code que nous écrivons. Nous essayons de faire en sorte que le code que nous rédigeons soit le moins complexe et le plus clair possible. Pendant longtemps, nous écrivions les programmes en langages procéduraux (C, Pascal, COBOL). Nous essayâmes quand même de garder les choses simples en séparant le code avec des noms de fonctions clairs et un fonctionnement le plus logique et compréhensible possible.

Cependant, le paradigme procédural a ses limites et ne convient pas très bien à une représentation du monde réaliste. Nous avions des fonctions qui englobaient d'autres fonctions qui manipulaient des structures qui sont des ensembles variables accessibles avec un nom. Peu à peu, alors que la programmation évoluait et que l'on rencontrait des difficultés à programmer des choses de plus en complexes, un nouveau paradigme est apparu et a été utilisé petit à petit par le grand public : le langage C++ a fait son apparition sur le marché.

Définition Wikipédia :

Un paradigme est une façon d'approcher la programmation informatique et de traiter les solutions aux problèmes et leur formulation dans un langage de programmation approprié.

En 1962, le paradigme objet est apparu. Il s'agit d'un paradigme qui représente des objets ayant des attributs et des méthodes. Ces attributs et méthodes décrivent la responsabilité et les fonctionnalités de l'objet en question. Ce paradigme est très proche de la réalité et rend l'approche d’une problématique complexe beaucoup plus abordable, car la conception en objets se veut claire et représentative de la réalité.

Ainsi, on peut modéliser une voiture qui a la responsabilité d'emmener d'un point A à un point B une ou plusieurs personnes, ainsi que d'autres objets. Mais une voiture seule a besoin d'autres objets qui assument d'autres responsabilités :

  • Un moteur qui fournit de l'énergie mécanique à la voiture

  • Des roues que fait tourner le moteur

  • Une carrosserie aérodynamique qui nous protège des effets de la vitesse

  • Des portes à ouvrir et fermer

  • Des sièges

  • Un frein à main

  • Un volant qui oriente les roues

  • Une pédale d'accélération

  • Une pédale de frein

  • Une pédale d'embrayage

  • Un levier de vitesse

Le moteur lui aussi contient d'autres objets :

  • Un couple

  • Un pot d'échappement

  • Un rotor

  • Un réservoir à essence

Nous ne sommes pas mécaniciens, mais avec la représentation d'une voiture en paradigme objet, les choses semblent déjà beaucoup plus simples à représenter. Une voiture est un objet composé d'autres objets qui permettent à la voiture de se comporter en tant que telle. Et lorsque tu roules dans une voiture, tu ne prends pas en compte toute la machinerie derrière et te contentes d'appuyer sur les pédales, d'orienter le volant et de passer les vitesses. Ta voiture roule et c'est tout ce que tu lui demandes (même si elle sait faire le café, tu ne l'achètes pas pour ça).

Ainsi, le modèle objet te permet d'obtenir un modèle réaliste de représentation des données. Ce modèle t'aide à concevoir proprement ton programme et ses composants, en séparant les responsabilités de chaque objet. Si un objet a plus d'une seule responsabilité, un autre objet va prendre le relais et assumer une autre responsabilité. Ta voiture a pour fonctionnalité de pouvoir orienter ses roues en interagissant avec le volant, mais ce qui se passe dans le module de direction est beaucoup plus compliqué et cette responsabilité est assumée par un autre objet.

Malgré l'aisance que permet la programmation orientée objet au niveau du design, ce paradigme est devenu populaire en 1983 avec la sortie de C++, surcouche du langage C qui ajoute les concepts du paradigme objet. A partir de là, la programmation a pris une nouvelle direction et tous les programmes ou presque sont aujourd'hui conçus, non plus en paradigme procédural, mais en paradigme objet, car il est bien moins complexe de concevoir des comportements grâce à lui.

Tu seras donc ravi d'apprendre que C#, qui est apparu en 2001 est complètement orienté objet. C'est-à-dire qu'il n'est pas possible d'écrire du code procédural (en théorie). Les fonctions étant un concept de la programmation procédurale, elles n'existent pas en C#. C# est composé de classes, elles-mêmes composées de méthodes, d'attributs et de propriétés. Le concept des structures en C# existe, mais il est surtout utilisé pour l'optimisation mémoire et la sérialisation, mais c'est un autre sujet.

A l'attaque du modèle objet !

↗
POO - Le concept et l'histoire
tutorialspoint.com

Une classe

Le paradigme objet est composé d'objets. Ces objets ont des propriétés : la carrosserie d'une voiture a une couleur, la voiture a un poids qui est celui de tous ses composants réunis, les sièges sont faits dans un certain matériau, les sièges peuvent être chauffants ou non ... Ces objets ont aussi des méthodes qui sont des actions que l'on peut réaliser sur les objets : on peut tourner un volant, appuyer sur une pédale, ouvrir une porte, s'asseoir sur un siège ...

Une figure géométrique projetée en 3D - un cube par exemple - a besoin d'un patron. Le patron est une surface géométrique plane qui permet de constituer la figure géométrique projetée en 3D par succession de pliages. Le patron est donc la base en deux dimensions d'une figure en trois dimensions. Eh bien, une classe est le patron d'un objet. Il s'agit de sa définition, sa spécification. Une classe définit quelles propriétés et quels comportements sont possibles pour un objet en question. Par conséquent, instancier un objet revient à instancier une classe.

Un objet n'est pas la même chose qu'une classe ?

A y réfléchir, c'est tout à fait logique. En effet, séparer un objet de sa classe est une opération que l'on fait tous les jours pour reconnaître des objets différents, mais de même classe. Tu peux avoir une table en verre et une autre en bois, ces deux objets seront tout de même des tables. Seule la connaissance de son patron te permettra de reconnaître qu'il s'agit d'une table. Voilà pourquoi un objet est toujours l'instance d'une classe.

Comment définit-on une classe alors ?

Tu as déjà utilisé les classes sans t'en rendre compte. La structure de base d'un programme en C# nécessite l'utilisation d'une classe qui contient le point d'entrée du programme, la fonction (ou devrais-je dire méthode) Main. Par conséquent, si on reprend la structure d'un code basique en C# :

cs
public class ClassName
{
    // Attributes, methods and properties of the class
}

La classe est identifiée par un nom qui, tout comme celui des fonctions ou des variables, doit être clair et représenter au mieux ce qu'elle contient et ses responsabilités. Ceci afin d'écrire un code clair et compréhensible pour une majorité de personnes. Le corps d'une classe est déclaré dans un bloc. Tout comme une boucle, une fonction ou une condition, et ceci afin de séparer le code que contient la classe du reste du code.

Et le mot public là, on en fait quoi ?

Laisse le mot-clé public de côté pour le moment, nous verrons sa signification précise plus tard. Retiens simplement que ce mot-clé signifie que ta classe est accessible ailleurs.

↗
Les classes en C#

Lire les paragraphes avant le premier bout de code

tutorialsteacher.com

Des attributs

Maintenant que nous avons vu à quoi servent les classes, que les objets sont des instances de cette classe et qu'ils peuvent être différents entre eux, attaquons-nous à un concept fondamental d'une classe : les attributs. Les attributs sont des états d'un objet. Ainsi, un téléphone a une couleur et un poids, il peut être allumé ou éteint, il a un nom et une marque. Toutes ces informations sont stockées dans des attributs. Les attributs embarquent des données qui caractérisent l'instance d'une classe. Ces attributs permettent de différencier les objets entre eux et de représenter leur état à un instant donné.

Les attributs sont en fait des variables attachées à une instance de classe. A l'image des variables, les attributs sont typés (puisque C# est fortement typé). Voici un code représentant une classe comportant uniquement des attributs :

cs
public class MobilePhone
{
    public int color; // Color as hexadecimal
    public string name; // Name of the device
    public string brand; // Brand that has manufactured the device
    public bool on; // Indicates if the phone is on or off
    public float weight; // Weight of the telephone
}

Ainsi, pour déclarer un attribut dans une classe, il faut préciser son type et son nom. Le mot-clé public, sans rentrer dans le détail, rend accessible ces attributs en-dehors de la classe (ce qui n'est pas considéré comme étant une bonne pratique).

Si les attributs décrivent les états d'un objet, mais qu'un objet embarque aussi un comportement, comment puis-je définir ses comportements ?

Des méthodes

Un objet met à disposition, en plus de ses attributs, un troisième concept : les méthodes. Les méthodes sont en quelque sorte des fonctions attachées à un objet. Une méthode est définie dans une classe et est exécutée sur un objet et uniquement sur un objet (il est toutefois possible de faire des méthodes de classe ...).

Une méthode décrit un comportement. Par exemple, on peut allumer ou éteindre un téléphone, on peut démarrer ou arrêter le moteur d'une voiture, on peut ouvrir ou fermer une porte, on peut remplir une bouteille d'un liquide. Bref, le champ des possibles s'ouvre et, grâce aux méthodes, on commence à pouvoir représenter des objets de manière réaliste, c'est-à-dire représentés comme des entités caractérisées par des attributs, avec lesquels on interagit grâce à des méthodes qui définissent des comportements auxdits objets.

Une méthode étant une fonction attachée à une instance de classe, elle possède les mêmes propriétés qu'une fonction. C'est-à-dire qu'une méthode dispose d'une signature qui comporte :

  • son type de retour (car oui, une méthode peut retourner une valeur)

  • son nom

  • les arguments qu'elle prend

Voici l'exemple d'une classe embarquant des attributs et des méthodes manipulant ces attributs:

cs
public class CarShop
{
    public int carsCount;
    public string address;
    public int vendorsCount;
    public float totalMoney;

    public void SellOneCar()
    {
        if (vendorsCount > 0 && carsCount > 0)
        {
            vendorsCount = vendorsCount - 1;
            carsCount = carsCount - 1;
            totalMoney = totalMoney + 100000;
            vendorsCount = vendorsCount + 1;
            Console.WriteLine("You have selled one car. Your now have " + totalMoney + " $");
        }
        else
        {
            Console.WriteLine("No vendor is free, you can not sell a new car ...");
        }
    }

    public void FireVendors(int firedVendorsCount)
    {
        if (vendorsCount - firedVendorsCount >= 0)
        {
            vendorsCount = vendorsCount - firedVendorsCount;
            totalMoney = totalMoney + (firedVendorsCount * 1000);
            Console.WriteLine("You have fired " + firedVendorsCount + " vendors. You now have " + vendorsCount + " vendors.");
        }
        else
        {
            Console.WriteLine("Please stay reasonable, you can not fire much more vendors that you already employ !");
        }
    }

    public void HireVendors(int employedVendorsCount)
    {
        if (totalMoney - (employedVendorsCount * 1000) >= 0)
        {
            vendorsCount = vendorsCount + employedVendorsCount;
            totalMoney = totalMoney - (employedVendorsCount * 1000);
            Console.WriteLine("You hired " + employedVendorsCount + " new vendors. You now have " + vendorsCount + " vendors in your team.");
        }
        else
        {
            Console.WriteLine("Insufficient money to employ " + employedVendorsCount + " vendors.");
        }
    }

    public void BuyOneCar()
    {
        if (totalMoney >= 50000)
        {
            carsCount = carsCount + 1;
            totalMoney = totalMoney - 50000;
            Console.WriteLine("You now have " + carsCount + " cars but you spent 50 000 $ to buy it.");
        }
        else
        {
            Console.WriteLine("Insufficient funds to buy a car.");
        }
    }
}

Oula, ça fait beaucoup de code !

Nous commençons à entrer dans le vif du sujet et nous pouvons débuter l'implémentation de trucs sympas grâce à la connaissance et l'utilisation du modèle objet. Mais d'abord, décortiquons un peu le code ci-dessus pour saisir les principes du paradigme objet. La classe écrite CarShop représente un concessionnaire vendant des voitures. Il dispose d'un nombre de voitures disponibles et d'un nombre de vendeurs employés. Nous manipulons ces attributs avec des méthodes qui interagissent avec notre objet de manière intuitive :

  • On peut vendre une voiture : SellOneCar()

  • On peut acheter une voiture : BuyOneCar()

  • On peut virer un ou plusieurs vendeurs : FireVendors(int firedVendorsCount)

  • On peut employer un ou plusieurs vendeurs : HireVendors(int)

Notre classe CarShop déclare les attributs nécessaires à sa caractérisation, qui sont le nombre de vendeurs, l'adresse du concessionnaire, le nombre de voitures disponibles, et l'argent total gagné. Les méthodes de la classe interagissent avec ses attributs pour fournir un fonctionnement logique basé sur l'état du concessionnaire. Ainsi, si ce dernier n'a plus assez d'argent, il ne peut acheter de nouvelle voiture, s’il n'a plus de vendeurs, il ne peut plus en virer, si trop de vendeurs sont occupés, aucune voiture aditionnelle ne peut être vendue ... Bref, la classe CarShop met à disposition un fonctionnement logique basé sur les valeurs de ses attributs.

Jolie, la classe ! Mais comment je l'utilise, maintenant ?

Instancier une classe

Un objet est une instance de la classe. Tu connais le mot-clé qui te permet d'instancier une classe, mais tu ne fais peut-être pas le lien. Souviens-toi des tableaux ... Comment fais-tu pour affecter un tableau à une variable de type tableau ? N'y a-t-il pas un mot-clé qui te permet de créer une instance du tableau ?

Euuuh ... new ?

Oui ! new est le mot-clé qui crée une instance de classe !

Alors, un tableau est une classe ?

Oui, et tous les types que tu manipules en C# sont en fait des classes.

Mais je n'utilise pas new à chaque type ? Par exemple, pour instancier un entier, je lui affecte simplement une valeur ?

Oui, et heureusement, d'ailleurs. Il existe des manières alternatives d'instancier des valeurs de certains types. Cependant, pour affecter une valeur à un entier, il serait possible d'écrire ceci :

cs
int myInteger = new Int32(12);

Ceci équivaudrait à :

cs
int myInteger = 12;

Tu admettras quand même que la deuxième instanciation est beaucoup plus facile à lire et à comprendre.

Si je reprends maintenant l'exemple de la classe CarShop de l'étape précédente, son instanciation donnerait :

cs
CarShop carShop = new CarShop();

Dans l'exemple ci-dessus, un objet de classe CarShop est maintenant instancié et affecté dans la variable carShop.

C'est bien, mais comment accéder aux méthodes et attributs ? C'est quand même à ça que sert un objet, non ?

Oui, aucun intérêt à manipuler des objets s’ils ne disposent ni de méthodes, ni d'attributs. En C#, un opérateur te permet d'accéder aux méthodes et attributs d'un objet : l'opérateur .. En effet, un simple point te permet de rentrer dans ton objet pour accéder à ses méthodes et attributs :

cs
CarShop carShop = new CarShop();
carShop.totalMoney = 51000; // Enough money to hire an employee and buy a car
carShop.HireVendors(1); // Hiring one vendor
carShop.BuyOneCar(); // Buying a car
carShop.SellOneCar(); // Selling the car bought

Analysons en détail le code ci-dessus :

  1. J'instancie un objet de classe CarShop dans la variable carShop

  2. J'affecte suffisamment d'argent à l'attribut totalMoney pour commencer la partie

  3. J'engage un vendeur

  4. J'achète une voiture

  5. Je la vends

  6. Mon total d'argent est de 100 000 $

Tu peux copier-coller le code de la classe plus haut et mettre le code qui l'utilise dans la méthode Main, pour jouer avec la classe CarShop. Cela te permettra de prendre en main correctement un objet avant de faire le challenge consistant à implémenter une classe et des méthodes. Mais libre à toi de t'y préparer ou non !

Challenge

Un jeu vidéo

Créer un personnage de jeu vidéo. La classe Character contient :

  • Un nom en attribut

  • Des points de vie en attribut

  • Une force d'attaque en attribut

  • Une force de défense en attribut

  • Une méthode IsAlive() qui renvoie un booléen indiquant si le personnage est vivant (true) ou mort (false)

  • Une méthode Attack() qui prend en paramètre un autre personnage et réalise l'opération suivante : attaqué.pv = (attaqué.pv) - (attaquant.force - attaqué.défense)

Mets en scène ces deux personnages s'attaquant, jusqu'à ce que l'un des deux meure. Lorsque l'un des deux mourra, affiche un message indiquant qui a gagné.

Critères de validation

  • Un lien vers ta solution sur Github
  • Un attaquant ne peut pas rajouter de points de vie à un attaqué
  • Lorsque l'un des deux personnages n'a plus de point de vie, le combat s'arrête