Bonjour,
Je suis aujourd'hui face à un problème qui me semble pourtant basique au niveau conception, mais aucune solution me satisfait pleinement.
Il s'agit d'une architecture de jeu vidéo qui se compose (entre autres) des classes suivante: Application, Display, InputManager, UpdateDispatcher et Scene.
L'Application est une classe qui, je pense, aggrége les classes InputManager, UpdateDispatcher et Display, pour en exposer les services à la Scene, qui utilisera chacuns de ces modules.
Je suis indécis sur la façon d'implémenter cette aggrégation. Plusieurs solutions s'offrent à moi. Je les liste ici en donnant ce qui me semblent être les points importants qui y sont associés.
L'agrégation en tant que membres avec accès direct:
Dans cette implémentation, on aurait (en gros):
Ainsi, un objet Scene ayant une référence sur Application pourra accéder à l'affichage, enregistrer des InputListener ou des UpdateListener directement après des classes dédiées.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 class Application { private: Display mDisplay; InputManager mInputs; UpdateDispatcher mUpdater; public: Display& getDisplay(); InputManager& getInputManager(); UpdateDispatcher& getUpdater(); };
Ce que je n'aime pas dans cette approche est en quelque sorte l'exposition de la structure interne d'Application vis-à-vis des services qu'elle rend. Scene n'a pas besoin de savoir que l'appel de sa fonction d'update se fait par une classe UpdateDispatcher. De plus, je serai obligé d'inclure le header d'UpdateDispatcher pour enregistrer une callable quelconque...
L'agrégation avec forwarding des interfaces:
Dans cette implémentation, la class Application possède les mêmes membres que précédemment mais ne les expose pas directement. A l'inverse, elle expose plutot les services rendus par l'intermédiaire de ces modules directement dans son interface.
Ainsi, j'ai plus l'impression qu'Application encapsule convenablement son implémentation et sa structure interne. En revanche, on risque d'assister à l'explosion de son interface ainsi que quelques autres détails d'implémentation que j'exposerai si on me le demande. Et peut-être qu'ici, Application rend plus de service qu'elle ne devrait... Mais il faut bien un noeud par où accéder aux autres services nécessaires quelque part!
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 class Application { private: Display mDisplay; InputManager mInputs; UpdateDispatcher mUpdater; public: void addModel(Model); void addInputListener(Callable pCallable); void addUpdateListener(Callable pCallable); };
L’agrégation par héritage: (*esquive les projectiles*)
Après la méthode précédente, le fait de devoir réexposer l'interface de chaque module m'a donné l'impression que l'héritage me priverai de ce fardeau. Peut-être est-ce un cas où l’agrégation par héritage serait pratique. Je ne suis par contre pas certain de pouvoir vraiment affirmer que l'Application est un UpdateDispatcher, même si elle est supposée rendre ce service.
Donc voilà, si vous avez d'autres proposition d'implémentation n'hésitez pas. Je ne suis pas vraiment décidé sur la méthode à implémenter, peut-être juste un penchant pour la seconde.
Est-ce qu'il me manque l'application d'une règle fondamentale de conception pour passer à travers ce dilemme?
Partager