Bonjour à tous,
Je reviens avec un problème similaire à celui ci : http://www.developpez.net/forums/d16...t-get-classes/ dont j'étais déjà l'auteur.
Pour résumé et vous éviter de (re)lire l'ancien post, mon projet se compose de :
* une petite dizaines de classes A, B, C ... qui représentent des objets spécialisés.
* une classe "core" qui s'occupe de faire évoluer en fonction du temps ma dizaine d'objets issus des classes A, B, C ...
* une classe "ui"
qui s'occupe de gérer les interactions clavier/souris/joysticks qui utilise "core"
par exemple, l'appui sur une touche du clavier exécute la fonction correspondante à l'action codée dans "core" représentée par la touche.
* une classe "menu"
qui s'occupe d'un menu textuel qui utilise "core"
j'ai un menu qui est composé d'une liste de commandes (représentées par des strings). lorsque l'on sélectionne une commande, la classe menu exécute la fonction correspondante à l'action codée dans "core" représentée par la string.
* une classe "Script_Executor"
qui s'occupe de gérer un langage de script fait maison qui utilise "core" (décrite schématiquement ci dessous)
par exemple, la lecture d'une ligne de script exécute la fonction correspondante à l'action codée dans "core" représentée par la ligne de script correspondante.
ma classe "core" ressemble à ceci
En gros, j'ai trois classes qui utilisent massivement les fonctions de la classe code qui elle s'occupe des 10 objets.
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
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 // A, B , C , D... environ 10 classes ayant chacune 3 à 7 paires de get/set et d'appels de fonctions publiques à la classe en question. class A { private: double m_a1; int m_a2; bool m_a3; public: A() : m_a1(0), m_a2(0), m_a3(false) {} double get_a1() const {return m_a1;} int get_a2() const {return m_a2;} bool get_a3() const {return m_a3;} void set_a1(double a1) {m_a1 = a1;} void set_a2(int a2) {m_a2 = a2;} void set_a3(bool a3) {m_a3 = a3;} void fctA1(...) {...} void fctA2(...) {...} }; class B { private: double m_b1; double m_b2; bool m_b3; public: B() : m_b1(0), m_b2(0), m_b3(false) {} double get_b1() const {return m_b1;} double get_b2() const {return m_b2;} bool get_b3() const {return m_b3;} void set_b1(double b1) {m_b1 = b1;} void set_b2(double b2) {m_b2 = b2;} void set_b3(bool b3) {m_b3 = b3;} void fctB1(...) {...} void fctB2(...) {...} }; // core a une instance de chacune des classes ci-dessus class core { private: A m_a; B m_b; // ... //fonctions spécifiques et privées de core public: double get_a1() const {return m_a.get_a1();} int get_a2() const {return m_a.get_a2();} bool get_a3() const {return m_a.get_a3();} double get_b1() const {return m_b.get_b1();} double get_b2() const {return m_b.get_b2();} bool get_b3() const {return m_b.get_b3();} // ... void set_a1(double a1) {m_a.set_a1(a1);} void set_a2(int a2) {m_a.set_a2(a2);} void set_a3(bool a3) {m_a.set_a3(a3);} void set_b1(double b1) {m_b.set_b1(b1);} void set_b2(double b2) {m_b.set_b2(b2);} void set_b3(bool b3) {m_b.set_b3(b3);} // ... void fctA1(...) { A.fct1(...);} void fctA2(...) {A.fct2(...);} void fctB1(...) {B.fct1(...);} void fctB2(...) {B.fct2(...);} void fctB3(...) {B.fct3(...);} void fctB4(...) {B.fct4(...);} // ... //fonctions spécifiques et publiques de core }; class Script_Executor { private: core& m_core; // ... public: Script_Executor(core& param) : m_core(param) {} double get_a1() const {return m_core.get_a1();} int get_a2() const {return m_core.get_a2();} bool get_a3() const {return m_core.get_a3();} double get_b1() const {return m_core.get_b1();} double get_b2() const {return m_core.get_b2();} bool get_b3() const {return m_core.get_b3();} // ... void set_a1(double a1) {m_core.set_a1(a1);} void set_a2(int a2) {m_core.set_a2(a2);} void set_a3(bool a3) {m_core.set_a3(a3);} void set_b1(double b1) {m_core.set_b1(b1);} void set_b2(double b2) {m_core.set_b2(b2);} void set_b3(bool b3) {m_core.set_b3(b3);} // ... };
J'avais déjà demandé de l'aide parce que core était embouteillé par trop de fonctions getClasseAMachin1() setClasseBMachin3() ... au point d'être illisible.
C'est toujours le cas.
J'ai écouté vos remarques, acheter le livre "coder efficacement Bonnes pratiques et erreurs à éviter (en C++)" et je me suis retroussé les manches.
La lecture du livre m'a permis entre autre de prendre connaissance de la loi de Demeter et des principes S.O.L.I.D. (au point de changer ma vie de programmeur.. Vraiment !!)
Maintenant mon application est un véritable chantier. Mon code se "clarifie" même si mes collègues voient surtout que je me focalise sur l'existant sans rien apporter de nouveau sur le court terme. Ce qui n'est pas la question.
J’ai parcouru mon projet, et j'ai recherché à appliquer le plus possible la loi S de SRP. Ce fut très enrichissant et intellectuellement stimulant.
J'ai modifié les RTTI qui servaient de tests de base en grand nombre.
Je suis allé à la chasse aux set/get discutables en m’efforçant de penser aux comportements et pas qu'aux données.
Mais je bloque sur le principe ISP.
Dans mon projet clairement, "core" apparaît comme un "god object" qui contient tout et qui s'occupe de tout. Je suis arrivé à une classe quigèresynchronise ses objets (en fonction du temps) ET qui propose aux autres classes utilisatrices "ui", "menu" et "Script_Executor" des fonctions passerelles afin de respecter la loi de Demeter. Typique:
dans mes classes utilisatrices, j'ai par exemple la fonction:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 void core::flipTimeClasseA() { return classeA->flipTime; }
Voila pour la présentation de mon problème. (Désolé de la longueur !!!! )
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 core->flipTimeClasseA();
Des solutions ?
J'ai cherché, je ne suis pas resté passif:
- j'ai cassé la loi de Demeter sur 2-3 objets [pour tester] et réalisé un
dans mes classes utilisatrices, j'ai écris :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 ClasseA* getClasseA() { return classeA; }
Option Core c'est le top, j'ai 15 fonctions en moins! Si je continue ainsi après 10 getClasseXXX, mon fichier " core" est enfin dépollué de tous ce qui m’embêtait alors. D'un point de vu personnel, j'ai rendu l'écriture du code plus complexe. Pour programmer la suite de l'application, il est nécessaire alors d'avoir en tête toutes les fonctions publiques ... la portée des fonctions étaient avant d'un niveau (exemple: classeA vers core) et passe maintenant à 2 niveaux (exemple: classeA vers Core vers UI) . Je suis en défaut par rapport au DIP
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 core->getClasseA()->flipTime();
Je n'en suis pas content.
- je crée une classe "core_acess"
C'est à dire une classe vide amie avec "core" qui reprend toutes les fonctions passerelles de core en accédant à la partie privée de "core" et qui sera alors l'intermédiaire entre "core" et les classes utilisatrices "Script_Executor", "menu", "ui".
Je n'en suis pas content non plus. j'ai l'impression alors de déplacer mon problème ailleurs et de ne pas mieux coller au principes du S.O.L.I.D.
Qu'en pensez vous ?
Que feriez vous à ma place ?
PS: merci d'être arrivé au bout de mon post, j'ai moi même cru ne pas y arriver.![]()
![]()
![]()
Partager