Cool Stack

Conception : Comment simuler un trait ou un mix-in en Java

Depuis le début Java interdit l'héritage multiple. A la place nous avons uniquement les interfaces et l'héritage simple. Il y a quelques temps je suis tombé sur un cas où l'héritage multiple m'a fortement manqué. Voici le cas d'utilisation :

héritage avec 4 classes enfants

J'ai 4 classes qui implémentent une même interface. Ces quatre classes font partie d'un framework. J'ai besoin d'ajouter 2-3 fonctionnalités dans ces classes pour les adapter à mon besoin. Mais je ne peut pas les modifier directement car elles font partie d'un framework. Je pourrais bien évidement hériter chacune de ces quatre classes et ajouter ces quelques fonctionnalitées à chacune de mes classes. N'aimant pas franchement le Dirty Coding et donc le copier/coller de code, j'ai préféré une petite astuce de conception à base d'interface et de composition.

Note: le framework que j'utilise m'interdit de modifier les constructeurs, car c'est lui qui instancie ces classes directement ; il m'est donc difficile d'utiliser un simple adaptateur.

Voici un schéma expliquant le principe avec 2 classe ControlerA et ControlerB :

java mixin en java avec heritage et interface

L'idée est la suivante :

  • la classe TraitControler contient le code des fonctionnalités
  • les classes ControlerATrait et ControlerBTrait héritent de ControlerA et ControlerB
  • les classes ControlerATrait et ControlerBTrait appelent le plus simplement possible les méthodes de TraitControler.

Le code de la class TraitControler :

class TraitControler{
    public void function1(){
        //un peu de code
    }
    public void function2(){
        //un peu de code
    }
}

Le code de la class ControlerATrait :

class ControlerATrait{
    TraitControler trait=new TraitControler();
    public void function1(){
        trait.function2();
    }
    public void function2(){
        trait.function2();
    }
}

idem pour le ControlerBTrait. Il est aussi intéressant de pouvoir appeler certaines méthodes de ControlerA(ou B), ou de ControlerATrait(BTrait) directement depuis une méthode de TraitControleur. Il faut donc avoir une interface commune à toutes les classe ControleurXTrait. Dans mon cas c'est IMyControler. Elle hérite de l'interface Controler qui est définit dans le framework. Si vous n'avez pas encore une tel interface, il faut en créer une avec toutes les methodes que vous souhaitez pouvoir appeler depuis votre trait. Pour pouvoir appeler les méthodes de votre objet depuis le trait, il faut créer un constructeur dans TraitControler qui vas prendre en paramètre un IMyControleur.

class TraitControler{
    IMyControler self;
    public TraitControler(IMyControler self){
        this.self=self;
    }
    ...
    public void function2(){
        ...
        self.onClick();
        ...
    }
}

Voila donc un manière de résoudre ce problème. Je ne prétend pas avoir la méthode la plus propre pour y répondre. Si vous avez des commentaires ou des solutions plus Classe n'hésitez pas à poster vos commentaire.

Posted Mer 09 novembre 2011 by Stéphane Planquart in conception