GNU/Linux >> Tutoriels Linux >  >> Linux

Tuto :Programmation Orientée Objet – Constructeurs et Héritage

Auparavant

Nous avons presque atteint la fin de notre voyage à travers ce tour bonus de notre introduction à la programmation orientée objet ! Dans notre dernier article, nous avons commencé notre exemple de zoo pour enfants avec un examen des classes, des objets et des méthodes. Si vous avez raté le début de cette série, vous pouvez nous rattraper ici. Sinon, replongeons-nous !


.

Constructeurs

Si vous avez fait l'exercice avec deux Dog objets, c'était un peu ennuyeux, non ? Après tout, nous n'avons rien pour séparer les chiens les uns des autres et aucun moyen de savoir, sans regarder le code source, quel chien a produit quel aboiement.

Dans l'article précédent, j'ai mentionné que lorsque vous créez des objets, vous appelez une méthode spéciale appelée constructeur . Le constructeur ressemble au nom de la classe écrit en tant que méthode. Par exemple, pour un Dog class, le constructeur s'appellerait Dog() .

La particularité des constructeurs est qu'ils sont le chemin d'accès à tout nouvel objet, ils sont donc un excellent endroit pour appeler du code qui initialise un objet avec des valeurs par défaut. De plus, la valeur de retour d'une méthode de constructeur est toujours un objet de la classe elle-même, c'est pourquoi nous pouvons affecter la valeur de retour du constructeur à une variable du type de classe que nous créons.

Cependant, jusqu'à présent, nous n'avons pas du tout créé de constructeur, alors comment se fait-il que nous puissions encore appeler cette méthode ?

Dans de nombreux langages, y compris C #, le langage vous donne un constructeur libre et vide sans que vous ayez à faire quoi que ce soit. Il est sous-entendu que vous voulez un constructeur; sinon, il n'y aurait aucun moyen d'utiliser la classe pour quoi que ce soit, donc les langages supposent simplement que vous en avez écrit un.

Ce constructeur invisible et libre est appelé le constructeur par défaut , et, dans notre exemple, cela ressemblera à ceci :

chien public(){ }

Notez que cette syntaxe est très similaire à Speak() méthode que nous avons créée précédemment, sauf que nous ne renvoyons pas explicitement de valeur et que nous ne déclarons même pas le type de retour de la méthode. Comme je l'ai mentionné précédemment, un constructeur renvoie toujours une instance de la classe à laquelle il appartient.

Dans ce cas, c'est la classe Dog , et c'est pourquoi lorsque nous écrivons Dog myDog = new Dog() , nous pouvons affecter le nouvel objet à une variable nommée myDog qui est de type Dog .

Ajoutons donc le constructeur par défaut à notre Dog classer. Vous pouvez soit copier la ligne ci-dessus ou, dans Visual Studio, vous pouvez utiliser un raccourci :tapez ctor et appuyez sur Tab deux fois. Il devrait générer le constructeur par défaut pour vous, comme illustré à la figure 9 :

Figure 9 :Ajout d'un constructeur avec "ctor"

Le constructeur par défaut ne nous donne en fait rien de nouveau car il fait maintenant explicitement ce qui était fait implicitement auparavant. Cependant, il s'agit d'une méthode, nous pouvons donc maintenant ajouter du contenu à l'intérieur des crochets qui s'exécutera chaque fois que nous appellerons ce constructeur. Et parce que le constructeur s'exécute comme la toute première chose dans la construction d'un objet, c'est l'endroit idéal pour ajouter du code d'initialisation.

Par exemple, nous pourrions définir le Name propriété de nos objets à quelque chose en ajoutant un code tel que celui-ci :

public Dog(){ this.Name ="Snoopy";}

Cet exemple définira le Name propriété de tout nouvel objet à "Snoopy".

Bien sûr, ce n'est pas très utile car tous les chiens ne s'appellent pas "Snoopy", alors changeons plutôt la signature de méthode du constructeur pour qu'il accepte un paramètre.

Les parenthèses des méthodes ne sont pas seulement là pour être jolies; ils servent à contenir des paramètres que nous pouvons utiliser pour transmettre des valeurs à une méthode. Cette fonction s'applique à toutes les méthodes, pas seulement aux constructeurs, mais faisons-le d'abord pour un constructeur.

Remplacez la signature par défaut du constructeur par :

chien public(string dogName)

Cet ajout nous permet d'envoyer une string paramètre dans le constructeur, et que lorsque nous le faisons, nous pouvons faire référence à ce paramètre par le nom dogName .

Ensuite, ajoutez la ligne suivante au bloc méthode :

this.Name =dogName;

Cette ligne définit la propriété Name de cet objet au paramètre que nous avons envoyé dans le constructeur.

Notez que lorsque vous modifiez la signature du constructeur, vous obtenez un cas de gribouillis rouges dans votre fichier Program.cs, comme illustré à la figure 10.

Figure 10 :Un cas de gribouillis rouges de notre nouveau constructeur

Lorsque nous ajoutons nos propres constructeurs explicites, C# et .NET ne créent pas implicitement un constructeur par défaut pour nous. Dans notre fichier Program.cs, nous sommes toujours en train de créer le Dog objets utilisant le constructeur sans paramètre par défaut, qui n'existe plus.

Pour résoudre ce problème, nous devons ajouter un paramètre à notre appel de constructeur dans Program.cs. Nous pouvons, par exemple, mettre à jour notre ligne de construction d'objet en tant que telle :

Chien monChien =nouveau Chien("Snoopy");

Cela supprimera les gribouillis rouges et vous permettra d'exécuter à nouveau le code. Si vous laissez ou définissez votre point d'arrêt après la dernière ligne de code, vous pouvez consulter le panneau Locals et vérifier que le Name de votre objet propriété a bien été définie, comme le montre la figure 11.

Figure 11 :Voir correctement notre ensemble de propriétés de nom

J'ai compris? Bien! Nous avons maintenant la possibilité de nommer notre chien, mais ce n'est pas vraiment un programme utile si les gens doivent le déboguer pour voir ce que nous faisons. Alors mélangeons un peu le code pour nous assurer que nous affichons le nom du chien qui aboie.

Mettez à jour votre Dog objets et modifier le Speak() méthode en tant que telle :

public void Speak() { Console.WriteLine(this.Name + " dit :Woof"); } 

La modification que nous avons apportée indique maintenant à WriteLine méthode pour concaténer le nom de cet objet avec la chaîne littérale « dit :Woof », ce qui devrait nous donner une sortie qui affiche mieux nos efforts. Essayez d'exécuter le programme maintenant et vous devriez voir quelque chose qui ressemble à la figure 12.

Figure 12 :Snoopy dit :Ouah

Bon travail! Vous pouvez maintenant créer de nombreux chiens avec des noms différents, et ils aboieront tous à votre commande.

Bien sûr, un zoo pour enfants avec seulement des chiens est quelque peu ennuyeux. Je veux dire, j'adore les chiens, mais peut-être pourrions-nous ajouter quelques animaux supplémentaires pour pimenter un peu l'expérience ?
.

Héritage

Commençons par ajouter un nouveau Cat classe à notre fichier Animals.cs. Ajoutez le code suivant à côté de la classe Dog, mais toujours entre les crochets de l'espace de noms PettingZoo :

class Cat{ public Cat(string catName) { this.Name =catName; } chaîne Nom ; public void Speak() { Console.WriteLine(this.Name + " dit :Miaou !"); }}

Ensuite, créons un Cat objet dans Program.cs et faites-le parler :

Chat monChat =new Chat("Garfield");monChat.Parle();

L'exécution de ce programme maintenant devrait vous donner une sortie ressemblant à la figure 13.

Figure 13 :Garfield dit :Miaou

Notre programme est prévisible et fonctionne, mais avez-vous remarqué à quel point les deux classes sont vraiment similaires ? Voyez combien de code nous avons dupliqué dans les deux classes ? L'orientation objet n'était-elle pas censée nous éviter d'écrire plusieurs fois du code ?

La réponse est oui. Nous pouvons – et devrions – simplifier ce code pour réduire le nombre de doublons. Ce que nous allons aborder démontrera une fonctionnalité d'OO appelée héritage .

L'héritage dans l'orientation objet vous permet de créer une hiérarchie de classes et de faire en sorte que chaque classe hérite des propriétés et des méthodes d'une classe parent. Par exemple, pour notre zoo pour enfants, nous pouvons créer un parent Animal classe et avoir Dog et Cat hériter de cette classe. Ensuite, nous pouvons déplacer des parties de notre code dans le Animal class afin que le Dog et Cat les classes hériteront de ce code.

Si, toutefois, nous déplaçons les parties en double de notre code vers cet Animal classe, puis le Dog et Cat les classes hériteraient chacune des mêmes propriétés et méthodes, faisant des enfants des clones des classes parentes. Cette organisation n'est pas très utile car nous voulons avoir des types d'animaux distincts. Ce serait terriblement ennuyeux si les chats, les chiens et tous les autres animaux étaient pareils. En fait, pour les besoins de notre programme, cela n'aurait aucun sens de les appeler différemment.

Maintenant, si l'héritage signifiait que nous ne pouvions créer que des classes enfants identiques à leurs classes parentes, il n'y aurait pas grand intérêt à faire tout cet effort. Ainsi, bien que nous héritions de toutes les parties d'une classe parent, nous pouvons également remplacer certaines parties d'une classe parent dans les classes enfants pour rendre les classes enfants distinctes.

Passons en revue cela et voyons comment cela fonctionne, d'accord ?
.

Créer des classes parent et enfant

Nous allons faire quelques pas en arrière et recréer le Dog et Cat cours d'une meilleure façon. Pour commencer, nous avons besoin d'une classe parent qui définira les propriétés et méthodes partagées des classes enfants.

Dans votre fichier Animals.cs, supprimez le Dog et Cat classes et ajoutez la définition de classe suivante :

class Animal{ chaîne publique Nom ; chaîne publique Son; public void Speak() { Console.WriteLine(this.Name + "says" + this.Sound); }}

Cette classe ressemble sans surprise au précédent Dog et Cat classes, avec les exceptions notées que nous avons rendu toutes les propriétés et méthodes publiques et que nous avons introduit une nouvelle propriété appelée Sound de type string . Enfin, nous avons mis à jour le Speak méthode pour utiliser le Sound variables.

À ce stade, votre Program.cs ne fonctionnera pas et, si vous cliquez sur cet onglet, vous devriez voir plusieurs messages d'erreur, ce qui est naturel car nous avons supprimé le Cat et Dog Des classes. Recréons-les et faisons-le en utilisant l'héritage. Dans votre fichier Animals.cs, juste après le Animal classe, ajoutez le code suivant :

class Chien :Animal { }class Chat :Animal { }

Ces nouvelles classes sont beaucoup plus courtes qu'auparavant et ne répliquent aucun code. La nouvelle syntaxe ici est un deux-points suivi du nom de classe Animal , qui indique à C# que nous voulons à la fois Dog et Cat hériter de l'Animal classer. En effet, Dog et Cat deviennent des classes enfants de Animal comme illustré dans le schéma de la Figure 14.

Figure 14 :Diagramme de l'héritage des classes d'animaux

Remarque :J'utilise le terme Propriété jusqu'à présent, ce qui est légèrement inexact car le terme correct est champ comme vous pouvez le voir dans le diagramme de la figure 14. Le champ est cependant moins clair en dehors du cadre de la programmation, j'ai donc utilisé la propriété à la place. Techniquement, une propriété est un wrapper autour d'un champ en C#.

.
Nous avons toujours un problème avec notre fichier Program.cs, cependant, à cause du constructeur personnalisé que nous avons créé. Si vous essayez d'exécuter ou de déboguer notre programme, vous devriez voir un message d'erreur indiquant que "'PettingZoo.Cat' ne contient pas de constructeur qui prend 1 argument" et un message similaire pour "PettingZoo.Dog".

Pour corriger cette erreur, nous devons rajouter les constructeurs. Cette fois, cependant, nous allons utiliser l'héritage pour hériter du constructeur et montrer comment vous pouvez étendre le constructeur pour modifier la fonctionnalité d'une classe parent.

Tout d'abord, nous devons créer un constructeur de base pour Animal , qui ressemblera aux constructeurs précédents que nous avons créés précédemment. Ajoutez le code suivant à votre Animal classe :

public Animal(string animalName){ this.Name =animalName;}

Comme précédemment, notre constructeur définit le nom de l'animal sur ce que nous lui passons. Cependant, cela ne suffit pas car nous devons également ajouter un constructeur à la fois au Dog et Cat Des classes. Nous l'utiliserons pour définir le son que chaque animal émet également.

class Dog :Animal{ public Dog(string dogName) :base(dogName) { this.Sound ="Woof"; }}class Cat :Animal{ public Cat(string catName) :base(catName) { this.Sound ="Meow"; }}

Pour les deux classes, nous ajoutons un constructeur qui accepte un Name paramètre. Nous définissons également le Sound de cet objet propriété au son que nous voulons que l'animal produise.

Cependant, contrairement à avant, nous appelons maintenant le constructeur de la classe parent et transmettons le Name paramètre du constructeur parent. Cet appel passe par le : base() et en ajoutant le dogName reçu ou catName paramètres entre parenthèses.

Remarque :Ce : base() La syntaxe est unique aux constructeurs. Nous verrons d'autres façons de modifier ou d'étendre la fonctionnalité de cas plus tard.

.
Avec ces mises à jour de notre code, nous pouvons maintenant réexécuter notre programme et voir un résultat similaire à la figure 15.

Figure 15 :Les animaux parlent, en utilisant l'héritage

Notez que nous n'avons ni Sound ni un Name propriété définie soit dans le Dog ou Cat Des classes. Nous n'avons pas non plus de Speak() méthode. Au lieu de cela, ils résident dans le parent Animal classe, et le Dog et Cat les classes héritent de ces propriétés et méthodes.

Nous pouvons également créer d'autres classes maintenant, pour n'importe quel type d'animal et nous n'aurons qu'à reproduire le modèle du Dog et Cat Des classes. Par exemple, nous pourrions créer ces classes :

class Parrot :Animal{ public Parrot(string parrotName) :base(parrotName) { this.Sound ="Je veux un cracker !"; }}class Pig :Animal{ public Pig(string pigName) :base(pigName) { this.Sound ="Oink"; }}

Ensuite, nous pouvons instancier ces classes dans Program.cs :

Parrot myParrot =new Parrot("Polly");myParrot.Speak();Pig myPig =new Pig("Bacon");myPig.Speak();

Nous aurons maintenant un véritable zoo entre nos mains lorsque nous exécuterons le programme, comme le montre la figure 16.

Figure 16 :Un zoo de quatre animaux qui parlent

Vous pouvez également créer d'autres sous-classes enfants de classes enfants existantes. Par exemple, vous pouvez séparer le Dog classe en Beagle et Pointer ou avoir le Parrot la classe hérite d'un parent Bird classe qui, à son tour, hérite de Animal . Cependant, la création de ce type de hiérarchie dépasse le cadre de cet article. Passons à autre chose et voyons enfin comment remplacer, modifier ou étendre les fonctionnalités des classes parentes dans les classes enfants.
.

Modifier l'héritage

Nous pouvons changer la méthode d'une classe parent en utilisant une technique appelée overriding . La syntaxe de remplacement peut différer d'un langage à l'autre, mais le principe de modification de la méthode du parent dans la classe enfant reste le même.

En C#, nous ajoutons le mot-clé override suivi de la signature de méthode que vous souhaitez remplacer. Nous devons également déclarer dans la classe parent que nous autorisons les enfants à remplacer les méthodes en ajoutant le virtual mot-clé à la signature de méthode dans la classe parent. (Les autres langues peuvent ne pas nécessiter cette indication supplémentaire.)

Dans notre programme, nous devons mettre à jour le Speak() méthode dans le Animal classe.

public virtual void Speak() { Console.WriteLine(this.Name + "says" + this.Sound); } 

Nous pouvons maintenant insérer notre remplacement du Speak() méthode dans le Dog class au début de notre bloc de code pour cette classe.

class Dog :Animal{ public override void Speak() { base.Speak(); Console.WriteLine("... puis court, pourchassant sa queue"); }[code restant omis]

Ce code nous indique que nous voulons remplacer ce qui se passe dans la méthode parent Speak() . Nous allons toujours exécuter la méthode parent en utilisant le base.Speak() call avant de sortir une ligne supplémentaire juste après.

Pourquoi voudriez-vous le faire? Eh bien, nous voulons peut-être que nos chiens soient aussi enthousiastes que possible en personne. Exécutez le programme et voyez par vous-même :

Figure 17 :Notre chien remplace sa méthode de classe

Tout d'abord, Snoopy aboie normalement :le base.Speak() la méthode appelle le Speak() méthode du parent Animal classer. Ensuite, le reste du code génère une deuxième ligne vers la console. En effet, nous étendons la classe parent en ajoutant de nouvelles fonctionnalités à la classe enfant uniquement. Voyez comment le Cat la classe n'est pas affectée.

Bien sûr, les chats sont notoirement difficiles à faire comme vous le souhaitez, alors reflétons cela dans le code. Mettre à jour le Cat classe et ajoutez le remplacement de méthode suivant, similaire au Dog classe.

class Cat :Animal{ public override void Speak() { Console.WriteLine(Name + " ne parle pas mais reste assis là à se demander quand vous allez le nourrir."); }[code restant omis]

Contrairement à ce que nous avons fait dans le Dog override, nous n'appelons pas le base.Speak() méthode cette fois. Sans cet appel, nous n'exécutons jamais le Animal Speak() méthode et changer complètement ce qui se passe dans cette méthode. Voyez par vous-même :

Figure 18 :Notre chat remplace également sa méthode de classe

Notez également que comme nous avons reconstruit nos classes, nous n'avons pas modifié le code Program.cs. Ces changements sont un bon exemple de la raison pour laquelle l'orientation objet est une technique très puissante; nous avons complètement changé le code sous-jacent sans toucher au programme qui utilise ce code. [L'orientation objet nous a permis d'isoler de nombreux détails d'implémentation et d'exposer simplement un petit ensemble d'interfaces avec lesquelles le programme doit interagir.]

Où nous avons contourné les règles

Avant de conclure, je tiens à souligner quelques points, principalement certaines "mauvaises" choses que nous avons faites.

Tout d'abord, dans nos classes, nous appelons la Console.WriteLine() méthode directement. Cette utilisation n'est pas une bonne décision de conception car les classes doivent être indépendantes de la façon dont nous publions les résultats des classes.

Une meilleure approche consisterait à renvoyer les données des classes à la place, puis à les produire dans le cadre du programme. Nous autoriserions alors le programme à décider quoi faire avec la sortie plutôt qu'avec les classes, ce qui nous permettrait d'utiliser les mêmes classes dans de nombreux types de programmes différents, qu'il s'agisse de pages Web, de Windows Forms, de QT ou d'applications console.

Deuxièmement, dans nos exemples ultérieurs, toutes les propriétés étaient public . Rendre ces propriétés publiques affaiblit l'aspect sécuritaire de l'orientation objet, où nous cherchons à protéger les données des classes contre toute manipulation de l'extérieur. Cependant, la protection des propriétés et des méthodes devient rapidement complexe lorsqu'il s'agit d'héritage, j'ai donc voulu éviter cette complication, du moins à ce stade d'introduction.

Enfin, il n'y a vraiment aucun intérêt à avoir Sound défini comme faisant partie du constructeur enfant, car nous dupliquons également ce code. Ce serait une meilleure conception de créer un constructeur dans la classe parent qui accepte à la fois un nom et un son, puis de l'appeler simplement dans le cadre du constructeur enfant. Cependant, encore une fois, par souci de simplicité, je ne voulais pas introduire de signatures de constructeur différentes à ce stade.

Ainsi, bien que nous ayons beaucoup appris dans ce didacticiel bonus, vous pouvez voir qu'il reste encore beaucoup à apprendre. Ne vous inquiétez pas trop pour l'instant, cependant. Vous pouvez prendre un moment pour vous féliciter d'avoir suivi tout le chemin. Si vous avez suivi toutes les étapes, vous avez appris :

  • Pourquoi utilisons-nous l'orientation objet
  • Comment créer des classes
  • Comment créer des propriétés et des méthodes
  • Comment créer des constructeurs et ce qu'ils font
  • Comment tirer parti de l'héritage et pourquoi il s'agit d'une fonctionnalité puissante de l'orientation objet

J'espère que vous avez apprécié cette leçon supplémentaire et que vous souhaitez en savoir plus. Assurez-vous de revenir avec nous pour découvrir de nouveaux contenus sur le blog Atlantic.Net et envisagez l'un de nos serveurs d'hébergement virtuel privé leader du secteur.
.


Linux
  1. Comment double-booter Linux et Windows

  2. Comment installer Elasticsearch et Kibana sur Linux

  3. Comment gérer et répertorier les services sous Linux

  4. Procédure :Réplication et configuration DRBD

  5. Guide pratique :programmation de sockets en Python

Comment installer et utiliser le langage de programmation R dans Ubuntu 20.04 LTS

Comment installer et utiliser le langage de programmation "R" sur Ubuntu

Comment installer et configurer Grafana

Comment :Introduction à la programmation - Variables, types et manipulation de données

Comment :Programmation orientée objet - Plus avec les classes et les objets

Comment mettre en réseau Ubuntu et Windows 10 ?