|
Publicité ' | ||||||||||||||||||||||||
|
|
#1 | ||
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 237 ![]() |
Bonsoir à tous,
J'ai trouvé un cas de mise en pratique de mes toutes petites connaissances en C++, c'est l'occasion pour moi de m'en servir et surtout de progresser dans la maitrise de ce langage. Plusieurs cours ont déjà été décortiqués mais j'ai encore du mal à faire des choix solides entre pointeurs et variables, allocation statique et allocation dynamique. Je souhaite surtout faire de l'objet, le plus rapidement possible. A première vue, et pour reproduire un comportement qui m'est familier avec des langages de bien plus haut niveau, je serais tenté de faire du "tout pointeur". Des attributs de mes classes aux paramètres qu'acceptent mes méthodes. Cependant, si mes attributs (privés) sont des pointeurs et que mes getters/setters acceptent aussi des pointeurs, on va vite se retrouver avec ce que je crois être un gros problème de fiabilité. Soit ce code : Code :
J'hésite vraiment entre ce risque là (avec des liaisons parfois franchement sales) et l'économie de mémoire qui me semble être substantielle en utilisant des pointeurs. Par extension, on peut également se demander si en cas de clone de mon objet de type A, la copie de l'attribut doit être prise en charge explicitement où si la duplication est récursive (d'une autre côté, pourquoi cela serait-il le cas vu que le seul attribut est un pointeur?). En d'autres termes, j'ai du mal à trouver une règle générale qui me permettrait de dire si le code est propre ou non. Merci de m'en dire plus, bon week end |
||
|
|
00
|
|
|
#2 |
|
Membre chevronné
![]() F5(){F5} Inscription : avril 2008 Messages : 456 ![]() |
hello,
1: private et public: - Dans bisounours, si tu as un attribut private, tu ne devrais avoir aucun accesseur (get/set), ta variable devrait être manipulée qu'à l'intérieur de ta classe. - j'évite de parler héritage pour l'instant. - interdire l'accès direct au publique et en accorder l'accès via des getters/setters peut se justifier si tu veux faire un traitement lorsqu'on modifie cette variable. (par exemple, compter le nombre de fois qu'on y accède pour des logs) =>de manière générale, si tu peux te passer des getters/setters, passe-t-en, parce qu'àprès tu trimballes des dépendances sur tes classes (qui au passage ne t'offre aucune utilité à part stocker) 2:services ou conteur de données: ya deux types de classes: celles qui stockent et celles qui servent. ex: classA{public:int a,b,c;}: ca stocke ex: classB{public:sayHello(){}}; ca sert (idem ya une valeur ajoutée). De manière générale encore, les classes qui stockent, tu duppliques leur valeur car tu t'en tapes. Pour les classes à sémantique d'entité, il n'y a pas de sens qu'elles soient duppliquées, (il faudrait que s'il y a deux classes duppliquées, elles aient la même durée de vie et le même comportement). Pour l'instant considère qu'elles sont pas copiables. 3:pointeur ou mémoire: Tant que tu le peux passes-toi des pointeurs sinon faut que gères toi même la mémoire.. Tu as besoin des pointeurs quand tu as besoin de faire de l'alloc dynamique (ex: un gros tableau, du polymorphisme). Sinon sers-toi de référence, ou de copie de données concernant les lois, pour getter/setter ya la loi de Déméter, le reste je sais pas. |
|
|
00
|
|
|
#3 | ||||
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 237 ![]() |
Bonjour galérien69, merci pour cette réponse.
Elle soulève de nouvelles interrogations chez moi Citation:
Je connais le principe de l'encapsulation qui m'impose private ou du moins protected pour chacun de mes attributs. Ceci dit, je fais des vérifications presque à chacun de mes set. Citation:
Citation:
Quand je parlais de copie, il s'agit bien sur des instances de ces classes Le but est d'éviter les conflits lors d'accès concurrents qui peuvent survenir en cas d'utilisation presque abusive des pointeurs. Citation:
Sur la question particulière des attributs, est-il préférable de les mettre en référence où en copie dure? Le problème va être pour le 1er cas une gestion correcte de la concurrence, cf le cas que j'évoque dans mon 1er post. |
||||
|
|
00
|
|
|
#4 | ||
|
Membre chevronné
![]() F5(){F5} Inscription : avril 2008 Messages : 456 ![]() |
Citation:
Citation:
Il n'y a pas de règles. Seulement des bonnes pratiques pour les lignes générales. Il y a des cas ou tu auras comme attributs des pointeurs: ex: construction d'un arbre, utilisation de polymorphisme, optimisation mémoire,... Il y a des cas ou tu auras des valeurs copiées/copiables. type de base (int,string,..), Ca dépend ce que tu fais. Enfin pour les accès concurrents, c'est assez délicat, je laisse le soin à d'autres. |
||
|
|
00
|
|
|
#5 | ||
![]() ![]() |
Salut,
En gros, tu peux avoir deux sortes de classes : Les classes qui ont une sémantique de valeur, qui peuvent exister plusieurs fois avec des valeurs identiques (comme une classe point, ou couleur par exemple) Ces classes sont, classiquement :
Ces classes sont, classiquement:
Il existe, comme l'a fait remarquer galerien69, la loi demeter qui dit que si un objet de type A manipule un objet de type B et que l'objet de type B manipule lui-même un objet de type C, tu ne devrais pas avoir à connaitre le type C pour pouvoir travailler avec le type A. A priori, tu ne devrais donc fournir un accesseur sur une donnée que si le fait de fournir l'accès à cette donnée fait partie des services que l'on est en droit d'attendre de la part de la classe : Par exemple, un compte bancaire a, très certainement, un membre "solde" qui représente le montant disponible sur le compte. Comme on s'attend à pouvoir connaitre ce montant, il est "sensé" de fournir un accesseur dessus Par contre, tout le monde est bien conscient qu'une voiture est composée, entre autres, d'un réservoir d'essence. Cependant, tout ce que l'utilisateur "habituel" d'une voiture doit savoir à son sujet tient dans "l'interface" que la voiture expose de ce réservoire : la trappe de remplissage, la jauge de niveau, les éventuelles indications permettant d'évaluer la distance que tu peux parcourir, etc: ta classe voiture va donc fournir une série de services relatifs à la présence du réservoir, mais il n'y a strictement aucune raison pour qu'elle donne directement l'accès à celui-ci En ce qu concerne les mutateurs, maintenant : Déjà, si tu n'expose pas un accesseur, il n'y a strictement aucune raison d'exposer un mutateur Et, si tu exposes un accesseur, la présence d'un mutateur se justifie très rarement :En effet, tu devrais préférer le fait d'exposer des comportement explicites, comme "ajouterAuSolde" ou "effectuerUnPayement", plutôt que d'exposer un mutateur "setSolde". La raison en est toute simple : si tu définis le mutateur "setSolde", cela obligera l'utilisateur à écrire un code proche de Code :
D'autre part, le choix du nom d'un comportement ayant pour objectif de modifier un objet permet bien souvent de lever certaines ambiguités : Si tu as une classe MachinChose qui se positionne en utilisant la classe point, le fait d'utiliser le comportement move(distanceX, distanceY) voir moveTo(nouvellePosition)permet bien mieux de se rendre compte qu'il s'agit d'un mouvement et qu'il peut ne pas être "immédiat" (car il faut sans doute le temps de parcourir la distance indiquée) que ne permet de le faire un simple "setPosition" Pour ce qui est de l'utilisation de pointeurs ou de références : Si tu veux (ou que tu dois) éviter la copie d'un objet lorsque tu le passes comme paramètre à une fonction, transmet le par référence "chaque fois que c'est possible" et par pointeur "lorsque tu n'as vraiment pas le choix". Les seuls cas dans lesquels tu devrait recourir aux pointeurs comme arguments d'une fonction sont:
Enfin, pour ce qui est des différentes variables : préfères autant que possible l'utilisation de valeur Si tu ne peux pas utiliser des valeurs, préfères les référence "chaque fois que faire se peut" et n'utilises les pointeurs que "lorsque tu n'as vraiment pas le choix" Les cas dans lesquels tu n'as pas le choix et qu'il faudra utiliser des pointeurs sont:
Enfin, pour ce qui est de l'allocation dynamique : Tu ne devrais avoir à écrire un new que dans le cas d'objets polymorphes Pour tout ce qui a trait à la gestion de collections d'objets, reportes toi aux classes prévues à cet effet
__________________
en bas de page
|
||
|
|
20
|
|
|
#6 | |||||||
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 237 ![]() |
Bonjour Galérien et Koala, vous m'avez gâté, merci
Je vais tenter de faire honneur à ces réponses de bonne qualité! A voir les différents traits que tu cites pour chacune, j'ai clairement plus une approche d'entité même si un faible nombre de mes classes peut rentrer dans une logique de valeur. Si je comprends bien et pour en être tout a fait certain, dans le cas de classes d'entité je ne dois pas pouvoir faire ça: Code :
Cette copie peut-elle être interdite formellement où elle reste une bonne pratique à laquelle il faut s'astreindre? Citation:
L'encapsulation m'oblige à mettre mes attributs privés mais pas à fournir des accesseurs/mutateurs dessus. C'est selon les besoins et à ma discrétion, suivant les cas évoqués après dans ton message. Citation:
Je viens principalement du java et du PHP, où la différence valeur/référence n'est pas du tout la même qu'en C++ et est altéré par le côté haut niveau des deux langages. En PHP il est possible de définir certains paramètres à NULL sans avoir à overloader mes méthodes avec plusieurs signatures (c'est obligatoire en Java). En C++, utiliser un paramètre de type pointeur me permettrais de le mettre à NULL si il est optionnel en vérifiant scrupuleusement bien sur dans la fonction sa valeur. Si je résume : référence = paramètre obligatoire, pointeur = possibilité d’être NULL. J'ai bien compris le reste. Citation:
Justement là on aurait des accès concurrent potentiellement sur les mêmes instances. Citation:
Citation:
Sachant qu'elles peuvent contenir des références plutôt que des valeurs, est-il recommandé de déclarer ces références comme constantes? Si on a accès à l'instance d'un membre de la collection, on pourrait éditer le contenu de la collection sans y avoir accès directement. Quoi que ce n'est pas exactement le contenu de la collection puisque la collection ne contient que des références. Whaaaaa c'est dur de savoir finalement Déclarer des collection de références ne servirait selon moi qu'à économiser de la mémoire, je n'ai pas d'autres usages en tête. En fait je pensais évoquer la concurrence de l'accès sur str1 : il peut être atteint depuis l'intérieur de la classe A mais aussi depuis le main directement puisque c'est là qu'il a été instancié. Avec ces explications supplémentaire, ce cas peut être évité en déclarant _attrib en valeur. C'est logique d'un autre côté, étant privé, il ne peut exister que dans l'instance. Sinon il devient publique dès lors qu'on peut le modifier en dehors de l'instance. Bonne soirée et merci encore. |
|||||||
|
|
00
|
|
|
#7 | ||||||||||||||
![]() ![]() |
Citation:
Très souvent, tu auras un petit nombre de classes ayant sémantique de valeur pour un grand nombre de classes ayant sémantique d'entité, et utilisant les classes ayant sémantique de valeur (de manière directe ou indirecte) Citation:
Citation:
Note que le gros intérêt supplémentaire à passer une référence est que tu profites alors du polymorphisme, ce qui est particulièrement utile pour les classes ayant sémantique d'entité Citation:
Si tu as un compilateur qui supporte cette nouvelle fonctionnalité (norme C++11), tu peux explicitement indiquer qu'une fonction est supprimée sous la forme de Code :
Citation:
Citation:
Citation:
![]() Elles peuvent contenir des objets (qui sont alors copiés) ou des pointeurs (qui ne sont en réalité que de variables numériques non signées qui contiennent l'adresse à laquelle trouver l'objet du type indiqué) Pour tout ce qui est de la gestion d'objets dont le type est une classe ayant sémantique d'entité, il faudra "forcément" passer par un pointeur, bien que l'on préférera (encore une fois) passer par un pointeur intelligent si on a la possibilité de travailler en C++11. Si tu n'as pas cette possibilité, il est *relativement* facile de se créer ce genre de classe, ou bien, tu peux aussi utiliser boost Citation:
Pour rappel, il est également possible de déclarer des fonctions membres comme étant constantes. Seules ces fonctions membres peuvent être appelées depuis un objet (ou une référence) constant(e). D'où le conseil que je donne Citation:
Mais il faut savoir que les collections de la STL utilisent toutes la notion d'itérateur dans une version constante et dans une version non constante. Si tu travailles sur une collection constante, tu ne pourras utiliser que l'itérateur constant. Par contre, si tu travailles sur une collection non constante, tu auras le choix entre le fait d'utiliser l'itérateur constant ou non Citation:
Et la principale raison pour le faire est de pouvoir profiter du polymorphisme, car on ne peut en profiter que si l'on travaille avec des références ou des pointeurs
__________________
en bas de page
|
||||||||||||||
|
|
10
|
|
|
#8 | ||||||
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 237 ![]() |
Bonsoir Koala01,
Ma compréhension progresse mais je fais encore face à quelques difficultés que je détaille ci-après. Citation:
Par contre dans le cas d'une classe avec des attributs privés de types complexes n'étant pas des référence, comment sa se passe? Je suis bien obligé de faire une copie à un moment, dans mon setter par exemple... à moins de convertir ces attributs privés en références mais ça pose le problème initialement évoqué pour moi. Idem - et même plus gênant - dans le cas de méthodes renvoyant une valeur de type complexe. Je vais logiquement initialiser une variable locale du même type puis la renvoyer. Sauf que renvoyer une référence ou un pointeur d'une variable locale ou temporaire ne va pas fonctionner, donc je suis obligé de faire une copie, ce que j’interdis dans ma classe -> Je suis bloqué? ![]() Citation:
Je travaille actuellement sous MSVC 2012 et j'ai une erreur de syntaxe sur le ';' à la fin des deux lignes "Entity(Entity const &) = delete;" et "Entity & operator = (Entity const & ) = delete;" et je ne comprends pas pourquoi. Citation:
Citation:
Merci de répondre à ces quelques dernières questions |
||||||
|
|
00
|
|
|
#9 | ||||||
![]() ![]() |
Citation:
Il faut donc bien que cet objet existe ... quelque part L'emplacement idéal de ce quelque part est, tout simplement, l'accessibilité privée De plus, tu ne devrais pas avoir de "setter"... le setter brise l'encapsulation en donnant accès à "un détai d'implémentation" de ta classe, et implique que l'utilisateur de ta classe prend une responsabilité qu'il ne devrait pas avoir. En effet, si tu travailles à coup de getter / setters, tu te retrouveras à avoir régulièrement un code proche de Code :
Il est donc beaucoup plus sensé de définir les comportements "qui vont bien" dans ta classe, de manière à ce que l'utilisateur n'ait qu'à fournir les indications qui permettent d'arriver à la bonne valeur. Par exemple : imaginons que tu aies une classe qui se positionne sur une fenêtre en fonction d'un point deux dimension. Tu ne devrais pas avoir un setPosition, mais plutôt un moveTo(newPosition), voir un move(distanceX, distanceY), qui auront l'énorme avantage d'expliquer réellement ce qui se passe, et qui peuvent donner une indication supplémentaire par rapport à setPosition : le fait que ce n'est peut etre pas "instantané". De plus, il faut penser qu'il existe une loi appelée "demeter" qui dit que si tu un objet de type A utilise en interne un objet de type B , tu ne devrais pas avoir besoin de connaitre B pour manipuler ton A. Par exemple, on se doute bien qu'une voiture dispose d'un réservoir d'essence. Mais il n'y aurait aucun sens à donner un accès direct à ta classe réservoir depuis ta classe voiture : A moins d'être mécanicien et de devoir le remplacer, tu n'as aucun intérêt à savoir où il se trouve exactement, et encore moins à y accéder directement. Ce qu'il te faut, par contre, c'est une interface, au niveau de voiture, qui te permet d'interagir indirectement sur ton réservoir, comme :
Citation:
Citation:
![]() Il y a encore, malheureusement, énormément de fonctionnalités de C++11 qui ne fonctionnent pas avec VC... Une recherche sur internet devrait te dire ce qui est supporté et ce qui ne l'est pas. Peut etre dans la version 2013 Citation:
Mais, s'il est clair que tu l'utiliseras souvent, j'espère pour toi que ce n'est pas celle que tu utiliseras le plus souvent, car elle pose énormément de problème en terme d'efficacité des comparaisons, par exemple, du simple fait qu'elle nécessite de comparer chaque caractère l'un après l'autre Ne me fais pas dire ce que je n'ai pas dit hein ? : la classe std :: string est tout ce qu'il y a de plus stable et de plus correcte en terme d'implémentation, simplement, comme toute chaine de caractères, elle subit les limites de ce genre de concept en ce qui concerne la comparaison. Si tu dois comparer quelque chose, le plus rapide est de le faire avec les type primitifs entiers
__________________
en bas de page
|
||||||
|
|
00
|
|
|
#10 | ||
|
Expert Confirmé Sénior
![]() ![]() Inscription : août 2004 Messages : 3 701 ![]() |
Citation:
Citation:
|
||
|
|
00
|
|
|
#11 | ||||||
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 237 ![]() |
Bonsoir
Citation:
Mais il est dans la voiture depuis le début... On peut aussi mettre toute sorte de choses dans le coffre de la voiture et en enlever. J'ai un cas où utiliser un setter me semble plus simple que de proposer un comportement imbriqué dans la classe. Je dispose de plusieurs classes me servant à créer des clients pour des services REST. Ces clients sont configurable au moyen d'un objet qui centralise le métier d'accès et d'écriture de la configuration dans divers fichiers. Ce que j'ai l'habitude de faire, c'est de créer une instance de ce gestionnaire de configuration, d'y ajouter la collection de fichier necessaires PUIS de créer une instance de mon client et d'utiliser un setter setCFG() sur mes clients pour leur préciser la configuration à utiliser. On peut accéder à cette configuration depuis un getter getCFG(). Donc dans ce cas, l'instance existe avant tout en dehors de l'objet qui n'est pas copiable. Donc un éventuel setter doit se contenter d'enregistrer une référence dans le client HTTP. Si il n'y a pas copie, je peux encore modifier depuis l'extérieur la configuration du client HTTP et cela me semble être une possibilité délicate. Je ne peux implanter le métier de gestion de la configuration directement dans le client car ce n'est pas le seul usage que je fais avec mon gestionnaire de cette configuration. Utiliser un setter me semble être le plus flexible. Citation:
Code :
Mais je ne peux pas non plus la copier. => Que faire? return ne fait pas de copie? Citation:
Je vais étudier tout ca plus en profondeur et voir en quoi consiste le conseil de r0d, merci à lui. Citation:
Je tenterai de faire avec! Bonne soirée |
||||||
|
|
00
|
|
|
#12 | |||||
![]() ![]() Cyrille Network programmer Inscription : juin 2010 Messages : 1 570 ![]() |
Citation:
Ou bien obj est un membre mutable et sert de "cache". SInon, on l'oublie trop souvent, mais: Code :
|
|||||
|
|
00
|
|
|
#13 |
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 237 ![]() |
Bien sur que je ne maitrise pas ce genre de concept.
Peut-être que si le haut niveau n'avait pas une philosophie différente, je les aurais intégré avec plus de rapidité. Bref, c'est comme ça. On peut se lancer dans ce genre de débat ou tenter d'apporter une réponse. Dans le code que je mettais en exemple dans mon précédent post, le but n'était pas foncièrement de vouloir copier un objet non copiable (je pas maso non plus). Néanmoins, compte tenu des bonnes pratiques données tout au long de ce post, j'écris une méthode qui me retourne une instance, créée localement, d'une classe d'entité. Comment je fais? Ça me semble bien compromis si on ne peut en retourner ni instance, référence ou pointeur. Je n'apprends pas le C++ sans but. J'ai des dizaines de classes écrites en Java ou en PHP où ce fonctionnement est présent. Je ne veux pas me prendre la tête et reprendre les comportements si possible en respectant quelques bonnes pratiques au passage. C'est pourquoi j'adopte des points de vue qui peuvent paraitre incongrus... |
|
|
00
|
|
|
#14 |
![]() ![]() Cyrille Network programmer Inscription : juin 2010 Messages : 1 570 ![]() |
Si tu crées en local l'élément que tu dois retourner, alors tu n'as pas le choix, tu le retournes par copie.
En vérité tu as une autre option, mais je crains que le grand Koala premier ou son acolyte gbdivers me tombent dessus mawashi en avant si je prononce ce mot : pointeur. ![]() Allez soyons seigneur, vous m'accorderez un smart pointeur ? ![]() Mais dans ce cas, la responsabilité de libérer la mémoire allouée revient à l'appelant qui récupère le pointeur alloué en interne de la méthode. JAVA et PHP sont de très mauvais exemples à ce niveau, les garbage collector te prennent par la main, et JAVA te camouflent toute l'implémentation réelle d'un objet (référence, pointeur, virtual, ...). Il faut savoir, qu'en gros, un objet JAVA est toujours une référence. Sinon tu as toujours l'option que j'ai donné plus haut.. Ou bien dans le cas de certaines factory, les éléments sont static et retournés par référence ou pointeur. |
|
|
00
|
|
|
#15 | ||||
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 237 ![]() |
Citation:
Citation:
Pensant que l'instance disparait lors de l'expiration de ce scope je trouve le warning justifié. Travaillant sous VC 2012 comme je l'ai dit, je ne sais pas néanmoins si c'est un caprice Microsoft ou la stricte norme C++. Citation:
Le côté positif c'est que ca va me permettre d'assainir un peu le code. Enfin, vu qu'on est tombé d'accord avec koala sur le fait de passer des références en priorité, cet aspect là ne me dépaysera pas trop. Citation:
Dans mon cas je préfère néanmoins autoriser la copie d'objet en théorie non copiables au lieu de charger mes classes en membres statiques. C'est un choix, espérons qu'il ne soit pas bloquant pour la suite. Merci et bonne nuit. |
||||
|
|
00
|
|
|
#16 | |
![]() ![]() |
En fait, tu dois te demander "pourquoi un objet est il non copiable"
Il y a deux raisons à cela : La première, c'est que tu veux être sur que ce qui arrivera à un objet particulier lui sera bel et bien appliqué (tu ne peux pas avoir cette certitude si l'objet existe en deux instances distinctes, dont l'une peut subir une modification dont l'autre ne serait pas au courent). La deuxième, c'est que tu te trouves dans une logique d'héritage : Si tu connais un client comme étant une personne et que tu fais une copie de "personne", tu vas sans doute perdre toutes les informations qui t'intéressent et qui on trait au fait que c'est aussi un client. Non, ce qu'il faut si une donnée est non copiable, c'est effectivement travailler avec des pointeurs (de préférence intelligent), non seulement pour profiter des possibilités de polymorphisme, mais, aussi, parce que cela permet d'être sur que leur durée de vie n'est pas limitée à la portée d'une fonction. Mais rien ne t'empêche de renvoyer, autant que possible, ce qui est pointé par le pointeur afin de renvoyer une référence, qui sera beaucoup plus sécurisante à l'emploi qu'un pointeur Citation:
Mais, au lieu d'invoquer directement la factory, tu devrait passer par l'objet qui gere les services REST pour lui dire "ajoutes un service qui contient telle et telle information". Tu "bazardes" toutes les informations brutes nécessaire à la création du service, éventuellement tu crées des fonctions membres qui prennent un identifiant de service et qui appliquent une modification ou l'autre, et c'est ton objet (au travers de la factory) qui s'occupe de gérer le tout. Cela te permettra de n'avoir même plus besoin de connaitre le service REST (ou ses classes dérivées) pour pouvoir en créer à demande : tout ce qu'il faut que tu saches, tenant dans les informations qui leur sont nécessaires. De la même manière, si ton objet gère un ou plusieurs service REST, tu n'as sans doute pas besoin de renvoyer d'office tout le service, mais ce peut très bien n'être que "certaines parties qui t'intéressent" pour un usage particulier. Et, là encore, tu demanderais à ton objet de te fournir telle information ou, s'il gère plusieurs services, telle information concernant le service répondant à tel identifiant. L'idée générale de ce que je propose n'est, en définitive, qu'une application stricte de la loi demeter : Si un objet A qui manipule un objet B en interne, tu n'as, a priori, aucun besoin de connaitre B pour pouvoir manipuler A, et tu ne fournis un accès à B que si "quelque chose" qui manipule A a explicitement besoin de connaitre B
__________________
en bas de page
|
|
|
|
00
|
|
|
#17 | ||||||
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 237 ![]() |
En fait pas tellement. Je l'ai bien compris et les deux raisons que tu évoques parlent d'elles-même.
C'est pour ça aussi que tout paramètre hors types primitifs seront références. Par contre dans le cas des instances locales à une fonction ça me pose le soucis que j'essaye d'expliquer depuis hier. Citation:
Code :
Peut-être y a-t-il une autre façon de l'écrire? Citation:
En terme de code ca donne pour l'instant quelque chose comme ça: Code :
En l’occurrence je conserve l'accès à l'instance cfg_mngr. Je pourrais supprimer le fichier que j'ai ajouté, avant de créer l'instance du client, avant de faire une requête. Si je tente de faire une requête ensuite, il me dira au mieux que ce que je demande n'est pas compatible avec la configuration. Au pire ça plante. Un tel système me permet d'avoir une configuration extensive, dont la structure n'est pas connue à l'avance. Je peux étendre RESTClient à l'infini et chacune des classes enfant peut avoir besoin de paramètres différents. Si je suis ta proposition, il faudrait que j'ai une "factory" pour chacune. Tiens, ces factory vont créer une instance de RESTClient, qui n'est pas copiable... cf le cas au début de ce post. ConfigManager me permet de configurer toute sorte de choses et vraiment pas que des RESTClient. Il faudrait donc qu'elle contienne des factory pour tout ce qu'elle permet de paramétrer. Est-ce cela la bonne solution? |
||||||
|
|
00
|
|
|
#18 | |||||
![]() ![]() Cyrille Network programmer Inscription : juin 2010 Messages : 1 570 ![]() |
Citation:
Tu alloues en statique, en fin de fonction obj est détruit, heureusement qu'il y a un warning pour te prévenir : tu retournes quelque chose qui n'exite pas. Code :
![]() Mais encore une fois, le problème selon moi c'est que de toutes façons il n'y a pas vraiment de raison à ce qu'une classe te retourne un nouvel objet de type entité. Ou alors ça s'apelle une factory. |
|||||
|
|
00
|
|
|
#19 | ||
|
Membre du Club
![]() Étudiant Inscription : janvier 2008 Messages : 237 ![]() |
D'accord c'est noté merci.
![]() Citation:
Citation:
|
||
|
|
00
|
|
|
#20 | |
![]() ![]() |
Citation:
Au SRP d'abord car, même si ta fonction ne fait que cela, et respecte donc le SRP au niveau de la fonction (ce qui n'est déjà pas si mal note En effet, même si la notion de responsabilité au niveau d'une classe est légèrement étendue par rapport à celle que l'on applique à une fonction dans le sens où les classes prennent généralement des responsabilités plus "génériques" (comprend : qui correspondent à plusieurs comportements particuliers), il est particulièrement difficile de trouver une responsabilité qui puisse englober celle de... créer un objet. A part une responsabilité de factory, dont c'est la responsabilité propre, tu pourrais envisager de donner la responsabilité de "gérer la durée de vie des objets" à ta classe, car on pourrait, éventuellement, considérer que la création d'un objet fait partie de la gestion de sa durée de vie, mais... Le problème avec demeter (et avec ISP) apparait ici dans le sens où une classe qui n'aurait qu'à gérer la durée de vie des objets devrait pouvoir se contenter de connaitre l'interface de la classe de base des objets dont elle gère la durée de vie. Or, si tu la rend, aussi, responsable de la création des objets dérivés, elle devra connaitre tous les types dérivés, et tu ne profiteras plus de cette ségrégation des interfaces. On peut d'ailleurs aussi estimer que tu ne respecteras pas "correctement" le principe ouvert fermé dans le sens où, chaque fois que tu créeras un nouveau type dérivé, tu devra venir modifier ton code pour le prendre en compte. Enfin, bref... Tout cela pour dire que l'idéal est vraiment de faire en sorte de respecter SRP au niveau de tes classes, en veillant à ce que chaque classe ne doive effectivement s'occuper que d'une seule chose, et donc en veillant à ce que, si tu donne une responsabilité de factory à une classe, elle n'ait rien d'autre à faire
__________________
en bas de page
|
|
|
|
00
|
Copyright © 2000-2013 - www.developpez.com