Java 1 Le pattern strategy Nous allons partir du principe que vous avez un code
Java 1 Le pattern strategy Nous allons partir du principe que vous avez un code qui fonctionne, c'est-à-dire un ensemble de classes liées par l'héritage, par exemple. Nous allons voir ici que, en dépit de la puissance de l'héritage, celui-ci atteint ses limites lorsque vous êtes amenés à modifier la hiérarchie de vos classes afin de répondre à une demande (de votre chef, d'un client etc.). Le fait de toucher à votre hiérarchie peut amener des erreurs indésirables, voire des absurdités : tout cela parce que vous allez changer une structure qui fonctionne à cause de contraintes que l'on vous impose. Pour remédier à ce problème, il existe un concept simple (il s'agit même d'un des fondements de la programmation orientée objet) : l'encapsulation ! Nous allons parler de cette solution en utilisant un design pattern (ou « modèle de conception » en français). Un design pattern est un patron de conception, une façon de construire une hiérarchie des classes permettant de répondre à un problème. Nous aborderons le pattern strategy, qui va nous permettre de remédier à la limite de l'héritage. En effet, même si l'héritage offre beaucoup de possibilités, il a ses limites. Posons le problème Mettez-vous dans la peau de développeurs jeunes et ambitieux d'une toute nouvelle société qui crée des jeux vidéo. Le dernier titre en date, « Z-Army », un jeu de guerre très réaliste, a été un succès international ! Votre patron est content et vous aussi. Vous vous êtes basés sur une architecture vraiment simple afin de créer et utiliser des personnages, comme le montre la figure suivante. Hiérarchie des classes Les guerriers savent se battre tandis que les médecins soignent les blessés sur le champ de bataille. Et c'est maintenant que commencent les ennuis ! Votre patron vous a confié le projet « Z-Army 2 : The return of the revenge », et vous vous dites : « Yes ! Mon architecture fonctionne à merveille, je la garde. » Un mois plus tard, votre patron vous convoque dans son bureau et vous dit : « Nous avons fait une étude de marché, et il semblerait que les joueurs aimeraient se battre aussi avec les médecins ! » Vous trouvez l'idée séduisante et avez déjà pensé à une solution : déplacer la méthode combattre() dans la superclasse Personnage, afin de la redéfinir dans la classe Medecin et jouir du polymorphisme ! La figure suivante schématise le tout. Déplacement de la méthode combattre() Java 2 À la seconde étude de marché, votre patron vous annonce que vous allez devoir créer des civils, des snipers, des chirurgiens etc. Toute une panoplie de personnages spécialisés dans leur domaine, comme le montre la figure suivante. Nouveaux personnages spécialisés Le code source de ces classes Personnage.java public abstract class Personnage { //Méthode de déplacement de personnage public abstract void seDeplacer(); //Méthode que les combattants utilisent public abstract void combattre(); } Guerrier.java public class Guerrier extends Personnage { public void combattre() { System.out.println("Fusil, pistolet, couteau ! Tout ce que tu veux !"); } public void seDeplacer() { System.out.println("Je me déplace à pied."); } } Medecin.java public class Medecin extends Personnage{ public void combattre() { System.out.println("Vive le scalpel !"); } public void seDeplacer() { System.out.println("Je me déplace à pied."); } public void soigner(){ System.out.println("Je soigne les blessures."); } } Civil.java public class Civil extends Personnage{ public void combattre() { System.out.println("Je ne combats PAS !"); } Java 3 public void seDeplacer() { System.out.println("Je me déplace à pied."); } } Chirurgien.java public class Chirurgien extends Personnage{ public void combattre() { System.out.println("Je ne combats PAS !"); } public void seDeplacer() { System.out.println("Je me déplace à pied."); } public void soigner(){ System.out.println("Je fais des opérations."); } } Sniper.java public class Sniper extends Personnage{ public void combattre() { System.out.println("Je me sers de mon fusil à lunette !"); } public void seDeplacer() { System.out.println("Je me déplace à pied."); } } À ce stade, vous devriez remarquer que : le code contenu dans la méthode seDeplacer() est dupliqué dans toutes les classes ; il est identique dans toutes celles citées ci- dessus ; le code de la méthode combattre() des classes Chirurgien et Civil est lui aussi dupliqué ! La duplication de code est une chose qui peut générer des problèmes dans le futur. Je m'explique. Pour le moment, votre chef ne vous a demandé que de créer quelques classes supplémentaires. Qu'en serait-il si beaucoup de classes avaient ce même code dupliqué ? Il ne manquerait plus que votre chef vous demande de modifier à nouveau la façon de se déplacer de ces objets, et vous courrez le risque d'oublier d'en modifier un. Et voilà les incohérences qui pointeront le bout de leur nez ! No problemo ! Tu vas voir ! Il suffit de mettre un comportement par défaut pour le déplacement et pour le combat dans la superclasse Personnage. Effectivement, votre idée se tient. Donc, cela nous donne ce qui suit : Personnage.java public abstract class Personnage { public void seDeplacer(){ System.out.println("Je me déplace à pied."); } public void combattre(){ System.out.println("Je ne combats PAS !"); } } Guerrier.java public class Guerrier extends Personnage { public void combattre() { System.out.println("Fusil, pistolet, couteau ! Tout ce que tuveux !"); } } Medecin.java public class Medecin extends Personnage{ public void combattre() { System.out.println("Vive le scalpel !"); } Java 4 public void soigner(){ System.out.println("Je soigne les blessures."); } } Civil.java public class Civil extends Personnage{ } Chirurgien.java public class Chirurgien extends Personnage{ public void soigner(){ System.out.println("Je fais des opérations."); } } Sniper.java public class Sniper extends Personnage{ public void combattre() { System.out.println("Je me sers de mon fusil à lunette !"); } } Voici une classe contenant un petit programme afin de tester nos classes : public static void main(String[] args) { Personnage[] tPers = {new Guerrier(), new Chirurgien(), new Civil(), new Sniper(), new Medecin()}; for(Personnage p : tPers){ System.out.println("\nInstance de " + p.getClass().getName()); System.out.println("***************************************"); p.combattre(); p.seDeplacer(); } } Le résultat correspond à la figure suivante. Résultat du code Java 5 Apparemment, ce code vous donne ce que vous voulez ! Mais une chose me chiffonne : vous ne pouvez pas utiliser les classes Medecin et Chirurgien de façon polymorphe, vu que la méthode soigner() leur est propre ! On pourrait définir un comportement par défaut (ne pas soigner) dans la superclasse Personnage et le tour serait joué. public abstract class Personnage { public void seDeplacer(){ System.out.println("Je me déplace à pied."); } public void combattre(){ System.out.println("Je ne combats PAS !"); } public void soigner(){ System.out.println("Je ne soigne pas."); } } Au même moment, votre chef rentre dans votre bureau et vous dit : « Nous avons bien réfléchi, et il serait de bon ton que nos guerriers puissent administrer les premiers soins. » À ce moment précis, vous vous délectez de votre capacité d'anticipation ! Vous savez que, maintenant, il vous suffit de redéfinir la méthode soigner() dans la classe concernée, et tout le monde sera content ! Seulement voilà ! Votre chef n'avait pas fini son speech : « Au fait, il faudrait affecter un comportement à nos personnages en fonction de leurs armes, leurs habits, leurs trousses de soin… Enfin, vous voyez ! Les comportements figés pour des personnages de jeux, de nos jours, c'est un peu ringard ! » Vous commencez à voir ce dont il retourne : vous devrez apporter des modifications à votre code, encore et encore. Bon : pour des programmeurs, cela est le train-train quotidien, j'en conviens. Cependant, si nous suivons les consignes de notre chef et que nous continuons sur notre lancée, les choses vont se compliquer. Un problème supplémentaire Attelons-nous à appliquer les modifications dans notre programme. Selon les directives de notre chef, nous devons gérer des comportements différents selon les accessoires de nos personnages : il faut utiliser des variables d'instance pour appliquer l'un ou l'autre comportement. Afin de simplifier l'exemple, nous n'allons utiliser que des objets String. La figure suivante correspond au diagramme des classes de notre programme. Modification de nos classes Vous avez remarqué que nos personnages posséderont des accessoires. Selon ceux-ci, nos personnages feront des choses différentes. Voici les recommandations de notre chef bien-aimé : le guerrier peut utiliser un couteau, un pistolet ou un fusil de sniper ; le sniper peut utiliser son fusil de sniper ainsi qu'un fusil à pompe ; le médecin a une trousse simple pour soigner, mais peut utiliser un pistolet ; le chirurgien a une grosse trousse médicale, mais ne peut pas utiliser d'arme ; le civil, quant à lui, peut utiliser un couteau seulement quand il en a un ; tous les personnages hormis le chirurgien peuvent avoir des baskets pour courir; Il va nous falloir des mutateurs (inutile de mettre les méthodes de renvoi (getXXX), nous ne nous servirons que des mutateurs !) pour ces variables, insérons-les dans la superclasse ! Bon ! Les modifications sont faites, les caprices de notre cher et tendre chef sont satisfaits ? Voyons cela tout de suite. Java 6 Personnage.java public abstract class Personnage { uploads/Litterature/ java-livre.pdf
Documents similaires
-
10
-
0
-
0
Licence et utilisation
Gratuit pour un usage personnel Attribution requise- Détails
- Publié le Jul 11, 2021
- Catégorie Literature / Litté...
- Langue French
- Taille du fichier 5.6792MB