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.