Notion d’état (2) – Classes et Énumérations

Dans l’article précédent je vous ai présenté la notion d’état avec ses motivations ainsi qu’un exemple de définition avec le jeu Pacman. Ici, je vous propose de commencer à représenter l’état à l’aide d’éléments informatiques comme les classes et les énumérations. Attention, il n’est toujours pas question de programmer pour l’instant, seul des schémas sont élaborés, avec le plus d’indépendance possible avec un langage et une architecture cible. Cette approche suit toujours le même principe: on commence toujours par raisonner autour de notions plus générales et abstraites avant de se perdre dans des détails d’implantation logicielle.

Représenter un élément

Reprenant l’exemple du jeu Pacman, le premier élément qui vient à l’esprit est naturellement le personnage Pacman lui-même. Sur la base de la définition “papier” de l’article précédent, Pacman est un élément du jeu qui a besoin en premier lieu de coordonnées x et y, ce qui peut être représenté par une classe avec deux attributs:

La définition papier indique également que tout élément doit posséder une information de direction (Aucune, Nord, Sud, Est ou Ouest). Dans ce genre de situation où il existe un nombre prédéfini de possibilités, il est fortement conseillé d’utiliser des énumérations. Ainsi, aucun risque d’utiliser des valeurs impossibles. Pour le cas des directions, une solution est:

Polymorphisme

Il existe d’autres personnages dans le jeu Pacman, à savoir les quatre fantômes. Une solution simple consiste à produire une nouvelle classe pour chaque cas. Cette solution n’est toutefois pas optimale, puisqu’on répète certaines informations dans toutes les classes, comme les coordonnées x et y. La bonne approche repose sur l’utilisation du polymorphisme de classes qui permet de regrouper des notions identiques dans une classe mère.

On peut commencer par extraire les éléments identiques dans une classe mère Element:

La classe Pacman ainsi formée fonctionne comme la précédente du point de vue de l’utilisateur de la classe. Les informations sont toujours présentes et accessibles via les mêmes méthodes (accesseurs et mutateurs vus ci-dessous). Pour le développeur, qui voit et modifie la partie immergée des classes (attributs, implantation des méthodes,…) les modifications sont bien sûr importantes.

Éléments fixes

Sachant qu’il existe également des éléments fixes (décors) et mobiles (personnages), le principe du polymorphisme peut être à nouveau utilisé pour introduire une classe MobileElement qui regroupe les informations propres aux éléments mobiles. En ajoutant les autres informations requises pour Pacman et les fantômes, le résultat suivant peut être obtenu:

Il ne reste plus qu’à ajouter les éléments fixes à notre conception pour clore la représentation des éléments. En répétant le principe du polymorphisme, on peut créer une classe StaticElement pour tous les éléments fixes, puis une classe fille Wall pour les éléments non franchissables et Space pour les éléments franchissables:

Types d’éléments fixes

Puis vient la question des différents types d’éléments franchissables et non-franchissables. Si le principe du polymorphisme est à nouveau suivi, une classe pour chacun de ces types est ajouté. Cela conduit à un grand nombre de classe, ce n’est pas un problème, dans les jeux et applications importantes il peut très bien y avoir des centaines sinon des milliers de classes rien que pour l’état. Cette approche peut toutefois être contestée car aucune classe fille de Wall ou Space n’a besoin de nouveaux attributs: elles sont toutes des classes totalement vides de définitions, sinon leur héritage. Il est conseillé dans ce genre de situation d’utiliser une seule classe munie d’un attribut qui définit le sous-type:

Ce principe de création de classe fille fonction de l’apparition d’attributs peut être également appliqué pour les méthodes. Si un nouveau type de données nécessite l’apparition ou la redéfinition d’une méthode, alors la création d’une classe fille est justifiée. Pour le cas des classes d’état (ou “données métier”), il n’y a pas de méthodes, puisque toutes les modifications d’état apparaissent dans une autre section (souvent appelée “logique métier”).

Assesseurs et mutateurs

Pour être bien précis, les classes d’état ont tout de même des méthodes, mais limitées à un type très particulier: les accesseurs (getters) et les mutateurs (setters). Les premiers permettent de consulter les données de l’état, et les seconds permettent de les modifier. Les consultations sont supposées simples, le plus souvent directement connectées aux attributs. Par exemple, pour obtenir la coordonnée x d’un élément, la méthode getX() est implantée:

public int getX() {
    return x;
}

Le même type de méthodes est produit sur le modèle getXXX() où XXX est remplacé par le nom d’un attribut.

La coordonnée x d’un élément peut être modifié avec la méthode setX():

public void setX(int x) {
    this.x = x;
}

Le même type de méthode est produit sur le modèle setXXX() où XXX est remplacé par le nom d’un attribut.

Les environnements de développement sont capables de générer automatiquement ces méthodes. En général, il n’est pas nécessaire de surcharger le diagramme de classes avec leur définition.

Pourquoi faire des assesseurs et des mutateurs

La réelle utilité des assesseurs et des mutateurs est une question légitime lorsqu’on débute en conception logicielle. En effet, il semble plus simple d’utiliser des attributs publiques, et de se passer de cette complexité inutile. Il existe de nombreuses raisons pour motiver ces méthodes, et la première d’entre elles est la nécessité de toujours séparer l’interface de l’implantation. L’interface dans ce cas est l’ensemble des méthodes assesseurs et mutateurs, et l’implantation le nom et le types des attributs. Tant que l’interface demeure inchangée, le développeur de l’implantation peut librement modifier les composants internes : par exemple modifier le type d’un attribut. Si un attribut est publique, il faut modifier toutes les parties du code qui utilisent cet attribut. Ces modifications peuvent être complexes en interne, mais encore plus lorsque vous avez des personnes externes à l’équipe locale de développement. Dans le cas d’une utilisation de getters/setters, il suffit de les modifier pour que le nouveau type de l’attribut soit pris en charge. Ce type de changement est courant dans la conception d’une application, il est donc toujours plus prudent d’utiliser ces méthodes, tout en sachant que les outils modernes permettent de les générer très facilement. En terme de coût calculatoire, les compilateurs actuels sont si performants que l’appel à ces méthodes est optimisé au point d’être inexistant.

Il existe bien d’autres motivations aux assesseurs et aux mutateurs que j’aurai l’occasion de présenter dans d’autres articles.

Ce contenu a été publié dans Tutoriel. Vous pouvez le mettre en favoris avec ce permalien.

Laisser un commentaire