Bonjour,
La question est dans le titre.
Mais je suppose que la raison pour laquelle je (me) pose cette question permettra d'affiner les réponses...
Je pense que j'ai compris l'intérêt du pattern « Non Virtual Interface », mais il y a quelques cas pour lesquels je me demande s'il est vraiment utile/nécessaire.
À chaque fois, il n'y a ni précondition, ni postcondition.
Un bout de code est souvent plus parlant qu'un peu de blabla...
Bon, je suppose que ce bout de code est suffisamment parlant que pour que voyiez là où je veux en venir.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 class base { public: virtual ~base() {} virtual bool is_cat1() const { return false; } virtual bool is_cat2() const { return false; } (...) virtual type1 get_value1() const { throw std::runtime_error("Invalid operation."); } virtual type2 get_value2() const { throw std::runtime_error("Invalid operation."); } (...) }; // class base class deriv1 : public base { private: type1 m_value; public: virtual bool is_cat1() const { return true; } virtual type1 get_value1() const { return m_value; } }; // class deriv1 class deriv2 : public base { private: type2 m_value; public: virtual bool is_cat1() const { return true; } virtual type2 get_value2() const { return m_value; } }; // class deriv2 (...)
Je précise tout de même que pour les autres services proposés par certaines classes filles, mais pas toutes, j'utiliserai le pattern NVI.
Mais pour ceux présentés ici, je ne suis convaincu de son utilité.
Pour le second cas, disons que l'on dispose d'une hiérarchie de foncteurs.
Quoi ? Cela vous paraît étrange ?
Hum...
Dans le premier exemple, où la seconde classe utilise des éléments (données ou fonctions) de la première, on aurait pu en faire des classes sœurs, certes.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 struct fonct1 { virtual return_type operator () (arguments_type... args) { (...) f1(...); (...) f2(...); (...) } protected: void f1(...); void f2(...); }; // struct fonct1 struct fonct2 : public fonct1 { virtual return_type operator () (arguments_type... args) { (...) f1(...); (...) f2(...); (...) } }; // struct fonct1 //******************// struct fonct3 { virtual return_type operator () (type arg0, arguments_type... args); }; // struct fonct3 struct fonct4 : public fonct3 { virtual return_type operator () (type arg0, arguments_type... args) { switch (arg0) { case ...: (...) break; case ...: (...) break; (...) default: return fonct3::operator()(arg0, args...); } (...) } }; // struct fonct4
Et dans ce cas, la classe mère aurait été attribuée d'un opérateur () virtuel pur.
Mais du coup, la question se pose aussi.
NVI ou pas NVI ?
Pour le second exemple, la seconde classe ajoute des comportements spécifiques pour des valeurs particulières à la première.
Bon, je me doute bien que dans un contexte autre que celui des foncteurs, on utiliserait sans hésiter le pattern NVI.
Alors pourquoi faire une exception ?
Eh bien, la principale raison d'être d'un foncteur est l'opérateur ().
À moins que l'opération intrinsèque soit vraiment complexe, on n'écrit pas grand chose à côté.
Bien sûr, une classe fille hérite de l'opérateur de la classe mère, s'il n'est pas redéfini.
Mais je me demandais si, par convention, on ne se contentait pas de rendre l'opérateur virtuel si l'on pouvait avoir besoin de le redéfinir dans des classes filles.
Bien entendu, comme précisé en début de message, on reste dans le cadre où il n'y a ni précondition, ni postcondition.
S'il y a des cas connus où il n'y a pas besoin du pattern NVI, je suis tout ouïe...
[Edit]
[/Edit]
Partager