Surcharge de méthode virtuelle ?

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour,

Je me suis enfin décidé à jeter un œil au Javaquarium, que j'ai décidé de réaliser en C++. J'en suis à l'introduction des poissons carnivores et herbivores (exercice 1.2 si vous avez le sujet sous les yeux).

Je pensais faire de ma classe Fish une classe abstraite en déclarant la méthode eat() virtuelle pure. Seulement, dans les classes Carinovorous et Herbivorous, les arguments de eat() ne sont pas les mêmes, puisque Carnivorous::eat() doit prendre en argument un poisson, alors que Herbivorous::eat() doit prendre une algue.

Je ne sais pas trop comment va se comporter le programme que je m'apprête à écrire, et malheureusement je ne trouve pas grand'chose sur Internet. Du coup, je suis un peu à la recherche de précisions sur ce point de détail. :/

Merci pour votre aide ! :)

Lu'!

Déjà un petit point, tu pourrais regarder du côté de la notion d'ISP (un des principes SOLID).

Quel est le point commun entre les algues, et les poissons : c'est des trucs qui peuvent être bouffés. Quel est le point commun entre les trucs qui mangent : ils bouffent tous des trucs qui peuvent être bouffés.

A partir de là, il y a la classe Edible (les trucs comestibles) et la classe Eater (les machins qui mangent).

Mais il faut encore résoudre la question de qui peut manger qui en fonction du type … des deux. Et c'est là que le bas blesse, le dispatching virtual n'est pas fait sur les deux à la fois. Par chance, les trucs qui mangent ne sont pas excessivement nombreux et obéissent à des règles assez simples. On peut faire un genre de double-dispatch à la sauce Visiteur en implémentant un truc de ce genre :

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <cassert>
#include <string>
#include <iostream>

class Carnivorous;
class Herbivorous;

class Edible{
public:
  virtual void recieveBite() = 0;

private:
  friend class Carnivorous;  
  virtual bool edibleByCarnivorous(Carnivorous const&) const{
    return false;   
  }

  friend class Herbivorous;
  virtual bool edibleByHerbivorous(Herbivorous const&) const{
    return false;   
  }
};

class Eater{
public:
  void eat(Edible& e){ 
    assert(canEat(e));
    e.recieveBite();
  }
  bool canEat(Edible const& e) const{
    return canIEat(e);   
  }

private:
  virtual bool canIEat(Edible const& e) const = 0;
};

class Named{
public:
  Named(std::string const& s) : name(s){}
  void show() const{ std::cout<<name; }

private:
  std::string name;
};

class Fish : public Edible, public Eater, public Named{
public:
  Fish(std::string const& s) : Named(s){}
};

class Seaweed : public Edible, public Named{
public:
  Seaweed() : Named("Seaweed"){}
  void recieveBite() override{}

private:  
  bool edibleByHerbivorous(Herbivorous const&) const override{
    return true;   
  }
};

class Carnivorous : public Fish{
public:
  Carnivorous() : Fish("Carnivorous"){}
  void recieveBite() override{}

private:
  bool edibleByCarnivorous(Carnivorous const&) const override{
    //en vrai : règle, je ne suis pas de la même espèce que l'autre.
    return true;
  }

  bool canIEat(Edible const& e) const override{
    return e.edibleByCarnivorous(*this);
  }
};

class Herbivorous : public Fish{
public:
  Herbivorous() : Fish("Herbivorous"){}
  void recieveBite() override{}

private:  
  bool edibleByCarnivorous(Carnivorous const&) const override{
    return true;
  }

  bool canIEat(Edible const& e) const override{
    return e.edibleByHerbivorous(*this);
  }
};

template<class Miam, class Miamed>
void edible(Miam const& a, Miamed const& b){
    a.show();

    std::string verb = a.canEat(b) ? "can" : "cannot";
    std::cout<<" "<<verb<<" eat ";

    b.show();    
    std::cout<<std::endl;
}

int main(){
  Carnivorous c;
  Herbivorous h;
  Seaweed s;

  edible(c,h);
  edible(c,s);
  edible(c,c);

  edible(h,c);
  edible(h,h);
  edible(h,s);
}

Le principe est de se dire que l'idée de manger quelqu'un/quelque chose c'est une mentalité de bouffeur et que c'est lui qui aura l'initiative de manger quelque chose. Or ce truc sait ce qu'il est (je suis un viandivore, je suis un verdurivore), donc quand il croise un truc mangeable, il lui demande, est ce que tu es mangeable par un (ce que je suis), et le truc en question peut alors lui répondre parce qu'il sait non seulement son propre type mais aussi celui de la bestiole qui lui pose la question.

(La classe named est là juste pour faire joujou avec l'affichage sans me prendre la tête dans le main, il faudrait au minimum que ce soit un CRTP, et surtout, l'opérator << c'est le bien).

Mon implémentation est assez simpliste, aussi s'il y a des spécialiste du double-dispatch / visiteur, qu'il se manifeste. Je n'aime pas ces patterns.

Édité par Ksass`Peuk

First : Always RTFM - "Tout devrait être rendu aussi simple que possible, mais pas plus." A.Einstein

+0 -0
Auteur du sujet

Whoah, je pensais pas du tout en arriver à ce genre de trucs (même si le tout est très logique) pour une question qui me paraisssait, à la base, drôlement simple.

En tous cas, cette histoire de SOLID m'a l'air vachement intéresante, il faudra que je me penche dessus. Je vais voir si j'arrive à mettre ça en place sans trop zieuter ton code. :-°

Au fait, je ne connaissais pas non plus le mot-clé override, qui peut éviter quelques bêtises de temps en temps. (La doc pour ceux que ça intéresse :) )

EDIT : À propos, pourquoi receiveBite est virtuelle pure ? Le traitement n'est pas le même selon le poisson ou l'algue mangée ?

Édité par Richou D. Degenne

Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte