POO : Réflexion conception
Répondre à la discussion
Affichage des résultats 1 à 15 sur 15

POO : Réflexion conception



  1. #1
    sebbbbbb

    Arrow POO : Réflexion conception


    ------

    Bonjour à tous,

    Je me questionne quant à la meilleure façon de structurer du code.

    Le contexte : Des clients et des factures. Classe Client, Classe Facture.

    La gestion des factures "standards" dans la classe facture mais des besoins pour certains clients de gérer différemment pour tout ou partie de la facture.

    J'ai pensé à créer des Classes Facture fille par client pour gérer le spécifique, classes qui hériteraient de la classe Facture.

    Ma question est alors la suivante :

    Comment structurer la chose pour éviter d'avoir un méga test pour instancier la bonne classe fille en fonction du client ou à défaut la classe Facture mère ?
    Comment proprement structurer la chose ?

    Merci pour vos idées.

    -----

  2. #2
    Merlin95

    Re : POO : Réflexion conception

    Est-ce que ça répond à votre question ? : https://fr.wikipedia.org/wiki/Fabriq...de_conception)

  3. #3
    pm42

    Re : POO : Réflexion conception

    Citation Envoyé par sebbbbbb Voir le message
    J'ai pensé à créer des Classes Facture fille par client pour gérer le spécifique, classes qui hériteraient de la classe Facture.
    Ce n'est pas une bonne idée. Une facture est une facture.
    Si un client veut la gérer différemment, ce n'est pas de la responsabilité de la facture mais du client.

    La bonne approche est d'avoir dans le classe Client une instance d'une autre classe appelée GestionnaireDeFacture.
    Et c'est cette classe qui a différents enfants en fonction des types de gestion.

    Quand le client veut gérer une facture, il délègue par un appel à son attribut gestionnaireDeFacture.

    De cette façon, on sépare bien les 3 concepts : le client, la facture et sa gestion.
    Un autre avantage est que si un client décide de changer sa façon de gérer les factures, on a juste à modifier son attribut.
    On ne change pas le type de client ou de facture ce qui est important quand stocke tout cela en base.

    Globalement, on évite autant que possible d'utiliser l'héritage en programmation objet moderne et on la remplace par la composition parce que cela augmente la flexibilité du code.

    Attention, l'héritage a encore du sens mais plus pour faire ce qui est décrit plus haut : des classes relativement simples qui implémentent différentes variantes d'une fonction.

    Parce que dès qu'on se retrouve avec des cas réels, l'héritage pose plein de problèmes. Il y a un petit exemple ici : https://www.synbioz.com/blog/tech/la...e-de-lheritage

  4. #4
    Merlin95

    Re : POO : Réflexion conception

    Je n'avais pas bien analysé mais je plussoie sur ce qu'a dit pm42.

  5. A voir en vidéo sur Futura
  6. #5
    Ikhar84
    Animateur Informatique

    Re : POO : Réflexion conception

    Je plussoies pm (que je salues), et ajouterais qu'avec juste les entités (au sens abstraction) Client et Facture, il faut jouer la composition et ajouter une entité qui representerait les lignes d'une Facture...

    À moins que vos factures sont mono-produit, par exemple, et encore, celanuira à la flexibilité du code justement, et à d'éventuelles futures demandes d'évolution que l'on rencontre souvent de la part des clients en tant que dév...

    En général, les questions comme celles-ci révèlent une mauvaise analyse, ou une mauvaise abstraction du problème (ce qui n'est pas une remarque "qualitative" sur le dév ! ).
    J'ai glissé Chef !

  7. #6
    pm42

    Re : POO : Réflexion conception

    Citation Envoyé par Ikhar84 Voir le message
    En général, les questions comme celles-ci révèlent une mauvaise analyse, ou une mauvaise abstraction du problème (ce qui n'est pas une remarque "qualitative" sur le dév ! ).
    Pour être indulgent, cela peut aussi venir du fait d'être débutant, ou d'avoir simplement lu les trucs standard sur la POO telle qu'on la concevait il y a quelques décennies et de ne pas être au courant des évolutions...

    J'ai repensé au cas initial. Supposons qu'on est modélisé différentes classes de factures suivant le client.

    On se rend compte qu'on a affecté par erreur une facteur au mauvais client. On veut donc changer cela.
    Et là, pas de chance : il faut détruire la facture pour recréer un objet d'un autre type correspondant au nouveau client.

    Avec la modélisation qui sépare le concept de gestion de la facture de celui de "la facture", on peut parfaitement déplacer une facture d'un client à un autre par contre.

  8. #7
    Ikhar84
    Animateur Informatique

    Re : POO : Réflexion conception

    Ce qui va encore plus me déranger, côté données, c'est comment implementer ce casse-tête sur une base ?
    Une "table" par type de facture (voilà la cohérence des données en cas de changement de "type" de facture pour un client...) ?
    Des champs avec des valeurs "pré-determinées" pour differencier les types de factures (et là on peut se poser la question de l'abstraction de l'entité Facture, si on peut définir un type de facture par de simple champs, ou encore, comment ajouter/gérer dynamiquement le type des factures) ?
    Avec des jointures multiples (re-voilà la cohérence et bonjour à la complexité) ?
    Etc ...

    Maintenant, on n'a aucune idée de ce que le PO entend par types de factures, au point d'en faire des entités héritées de Facture ?

    Edit: un bon vieux diagramme de classe des-dites facture aiderait bien ici !
    J'ai glissé Chef !

  9. #8
    pm42

    Re : POO : Réflexion conception

    Citation Envoyé par Ikhar84 Voir le message
    Ce qui va encore plus me déranger, côté données, c'est comment implementer ce casse-tête sur une base ?
    Une "table" par type de facture (voilà la cohérence des données en cas de changement de "type" de facture pour un client...) ?
    C'est le problème du mapping objet-relationnel et notamment de l'implémentation de l'héritage et oui, ce n'est pas simple.

    Il y a différentes techniques et outil pour le faire, c'est bien supporté par les trucs comme Hibernate mais malgré tout, il est facile de terminer avec un plat de spaghettis et des requêtes lourdes et pas les plus rapides.

    Sinon, on peut utiliser des bases NoSql comme MongoDB en étant conscient des limitations.
    Ou faire un modèle objet simple en ne rendant persistants que des types algébriques et en utilisant uniquement la composition, pas l'héritage.
    Si on veut des objets plus sophistiqués en mémoire, on les reconstruit à la volée avec des DAO.

    Sinon, j'ai tendance à bien aimer les approches hybrides : on stocke les méta-attributs (id, clé externe...) dans des colonnes et le reste en sérialisé, dans une colonne json par exemple.

    Mais il n'y a pas de réponse qui corresponde à tous les cas ou même à tous les volumes de données. Certains très gros systèmes utilisent d'ailleurs des combinaisons de tout cela avec différents types de base pour différentes données.

  10. #9
    umfred

    Re : POO : Réflexion conception

    En général, c'est le vendeur qui spécifie la facture (son format) qu'il émet à son client, pour que le vendeur se retrouve dans ses factures.
    Dans le principe, comme évoqué mais énoncé d'une autre façon, il faut séparer la facture-données (éléments de la facture) de la facture-format (présentation des données)

  11. #10
    Ikhar84
    Animateur Informatique

    Re : POO : Réflexion conception

    @Umfred: oui, c'est ce que j'entendais par élever le niveau d'abstraction.

    @pm: mon coordo me pousse vers le no-code et noSql... et en particulier vers la stack MEAN, avec une certaine... inertie... de ma part, je l'avoues...

    Je suis partisan de DAO (PHP et Java par les temps qui courent) et, suivant les techno, d'ORM, en particulier sous Java (Spring)
    Dernière modification par Ikhar84 ; 09/02/2021 à 17h31.
    J'ai glissé Chef !

  12. #11
    pm42

    Re : POO : Réflexion conception

    Citation Envoyé par Ikhar84 Voir le message
    @pm: mon coordo me pousse vers le no-code et noSql... et en particulier vers la stack MEAN, avec une certaine... inertie... de ma part, je l'avoues...
    Je suis partisan de DAO (PHP et Java par les temps qui courent) et, suivant les techno, d'ORM, en particulier sous Java (Spring)
    Tout ça, c'est juste des outils et plus on a d'outils maitrisés dans sa trousse, plus on sait traiter de façon efficace un plus grand nombre de sujets.
    Si on a juste un marteau, on termine à l'utiliser pour taper sur des vis.
    Mais si on passe son temps à ajouter de nouveaux outils, on ne maitrise rien et on joue au lieu de travailler.

    Dans ton cas, je ne sais s'il a raison ou pas : la stack MEAN est intéressante et permet sans doute de coder un peu plus vite que ce que tu fais.
    J'aime bien l'idée d'utiliser le même langage coté front et back, notamment en utilisant Typescript pour ne pas se faire avoir par le coté non typé de Javascript. Cela simplifie la vie (en ce moment, je fais du Typescript + Scala et l'interface entre les 2 en JSON est une source d'erreurs).

    Et je ne suis pas fan de PHP et je trouve Java verbeux et à la limite du supportable aujourd'hui mais j'en ai fait pendant 20 ans et on a pu construire un soft de plus de 1 million de lignes avec.
    Donc ce que tu fais marche, est très fiable et tu peux continuer avec. Mais c'est vieillissant.

  13. #12
    sebbbbbb

    Re : POO : Réflexion conception

    Merci à tous pour vos réponses détaillées ! Je suis semble-t-il paumé !

    Je vais essayer de clarifier ma demande, cela vous permettra peut être d'identifier plus précisément les connaissances et les concepts qui me manquent

    Je pense que nous pouvons laisser de coté la base de données : une facture sera toujours représentée par une entête et des lignes de différents types (commentaire, valorisée etc...). La question de la spécificité relève ici de règles de gestion différentes à implémenter au niveau de la génération de ces éléments, pas du type des éléments générés.

    Ex : Un article vendu pour un client pourra être représenté par une ligne de facture de type valorisée alors que ce même article pour un autre client générera 2 lignes, 1 valorisée et 1 de commentaire.

    Autre ex plus parlant : Admettons une gestion différente des remises d'un client à l'autre. En fonction des clients, une remise globale se calculera sur la facture selon des critères différents : L'un par montant acheté, l'autre par nombre d'article acheté, un dernier par des règles bien plus complexes et spécifiques. Une ligne de remise viendrait s'ajouter en pied de facture (ou pas), avec des calculs différents pour chaque client.

    A un moment, dans mon contrôleur qui gère la création de cette facture pour ce client, je ne visualise pas de solutions qui me permettent d'éviter un IF THEN ELSE ou SWITCH géant pour instancier un OBJET spécifique, que ce soit mon client ou ma facture qui gère les spécificités.

    @Merlin95 : Même dans le design pattern factory, ma question est la même. Je peux déléguer l'instanciation de ma classe à une factory, je ne vois pas comment éviter ce test ingérable si j'ai des centaines de client.

    @pm42 : De même dans mon GestionnaireDeFacture (qui a différent enfant) propriété de mon client, quand je vais instancier mon client, je vais bien devoir instancier le bon enfant gestionnaireDeFacture en fonction du client que je traite. Comment faire cela proprement si j'ai des centaines de clients avec chacun du spécifique à gérer ?

    @Ikhar84 : J'avoue être perdu par la composition... Dans mon exemple, dans quel objet mettre ma méthode gestionRemiseGlobale() qui gérerait les cas spécifiques par client ? Et une dernière fois promis , comment la composition me permettrait d'éviter de devoir instancier une classe d'un type spécifique en fonction du client que je traite, qu'il s'agisse de ma classe client, facture ou gestionnaireDeFacture ?

    Merci encore pour votre aide

  14. #13
    Merlin95

    Re : POO : Réflexion conception

    Ha marrant, j'ai fait bcp de SGBD mais je préfére idéalement le nosql.

  15. #14
    Merlin95

    Re : POO : Réflexion conception

    Regarde tu passes l'id du client à la BD puis tu récuperes de la base de données puis de ton mapping l'objet décrivant "comment gérer ta facture".

  16. #15
    pm42

    Re : POO : Réflexion conception

    Citation Envoyé par sebbbbbb Voir le message
    A un moment, dans mon contrôleur qui gère la création de cette facture pour ce client, je ne visualise pas de solutions qui me permettent d'éviter un IF THEN ELSE ou SWITCH géant pour instancier un OBJET spécifique, que ce soit mon client ou ma facture qui gère les spécificités.
    Je me trompe peut-être mais j'ai l'impression que tu mélanges classe et instance.
    Donc on va dire que tu as :
    - des classes qui contiennent des données
    - des classes qui contiennent la logique métier

    Normalement, en POO "de base", on met la logique métier et les données dans les mêmes classes et si nécessaire, on fait de l'héritage mais comme déjà dit, cela a des limites.

    Dans ton cas, tes classes données sont :
    - client
    - format facture client
    - remises client
    - facture

    et peut-être des trucs pour représenter ce qui est vendu.

    Et tu as une classe GestionnaireFacture qui contient une méthode créeFacture(bien, quantité, client, remiseclients, formatfactureclient).
    C'est cette classe là dont tu peux avoir plusieurs versions pour avoir de la flexibilité mais tu ne vas pas en avoir une par client.

    Pour commencer, tu vas en avoir une seule et elle va générer toutes les factures.
    Tu vas décomposer ce que c'est qu'une facture et avoir une méthode qui renvoie chaque composant de la facture dans GestionnaireFacture. Chaque composant (en-ête, commentaire, remise, etc) est optionnel.

    La méthode créeFacture va donc appeler les différentes méthodes qui créent les composants et assembler les résultats renvoyés pour créer la facture.
    Typiquement, crééFacture est publique alors que toutes les autres, creeEntete, calculeRemise, ajouteCommentaire sont protected.

    Quand tu ajoutes un nouveau client, tu ajoutes aussi une instance de "format facture client" qui contient son paramétrage et une instance de "remises client" qui a toutes les infos pour calculer une remise.

    Donc tu ne crée pas une classe par client mais une instance des différentes classes. A la limite, on pourrait tout mettre dans la classe Client mais elle deviendrait grosse et compliquée à maintenir.

Discussions similaires

  1. Conception mécanique - 3 erreurs de conception
    Par BlouBlou98 dans le forum Physique
    Réponses: 8
    Dernier message: 13/05/2020, 18h27
  2. Réflexion d´ultrason et surface de réflexion.
    Par FlorianCribier dans le forum Physique
    Réponses: 2
    Dernier message: 29/01/2019, 13h03
  3. Réponses: 6
    Dernier message: 19/11/2010, 14h53