Partage
  • Partager sur Facebook
  • Partager sur Twitter

Le « S » de « SOLID »

Merci de m’aider à concevoir

Sujet résolu
    3 août 2019 à 12:34:05

    Avec l’été et les départs en vacances des chefs, je vais avoir un peu de temps pour réfléchir. Je début  a la programmation objet (Mais pour faire des GUI, il faudra bien que je m’y mette!). Alors autant commencer en faisant bien, sur un petit outil batch !
    Il me faudrait réaliser un outil logiciel, dont l’objectif est de 1) lire un fichier « descripteur » (XML, ou autre), 2) vérifier/modifier les données, 3) Sauvegarder dans un fichier, les données au format « ALaCon » imposé.

    Ma première idée était de faire une conception du style :
    J’ai une « classe » qui représente la structure de donnée (Plusieurs listes avec différents type de donnée plus ou moins complexe), avec 3 fonctions membres : Une pour la fonction 1) [je lis le fichiers descripteur, à chaque foi que je rencontre donnée, je l’ajoute à cette « classe »], une pour la fonction 2) [je parcours mes listes, et je vérifie les éléments (suppression des doublons, vérification des validités, ….)], et une pour la fonction 3), [écriture de ces listes dans le fichier destinataire (en respectant la syntaxe à la …)].

    Tout irait pour le mieux, sauf que ça ne respecte pas le « S » de SOLID (Single Responsibility Principle).
    Si j’ai bien compris, il faudrait que je coupe cette classe en 3 : Une pour chaque fonction : 1) Lecture, 2) Vérification/Modification, et 3) Écriture. Mais dans ce cas, comment faire pour que la classe « lecture » passe les données lues à la classe « Modification » et que la classe « Modification » passe ses données vérifiées à la classe « Écriture » ?

    Si je fais un constructeur de la classe « Modification » qui récupère toutes les donnée de la classe « Lecture », il faut donc que j’expose toute la structure interne de la classe « lecture » et que je la duplique dans la classe « Vérification ». Ça ne me paraît pas souhaitable.

    Faudrait-il que je fasse hériter mes 3 classes d’une classe mère qui ne comporte que les données (la liste des cibles, la liste des fichiers, ...), et que pour chacune des 3 classes filles je fasse un constructeur avec la classe mère ?
    C’est ce que j’ai maquetté, mais ça ne marche pas ? [BUG?]

    Certainement que cette question est triviale (ou usuelle), mais pour moi, pôve débutant en POO, c’est pas encore compris. Merci si vous pouvez me donner des idées.

    Mes sources (qui ne fonctionne pas, je ne récupère pas le bon nombre d’élément)

    Main.CPP

    #include <iostream>
    #include "tests.h"
    
    using namespace std::literals::string_literals;
    
    int main()
    {
    
        tTruc truc;
        // 1) lecture fichier
        tTrucRead trucRead {truc, "MonFichierEstLePlusBeau"s};
        // 2) Modification
        tTrucModif trucModif {truc};
        trucModif.add (1);
        trucModif.add (2);
        std::cout << "nb elem in vector : " << trucModif.nbParam () << std::endl;
        // 3) Sauvegarde ... enfin ici, seulement verification que cà marche
        tTrucWrite trucWrite {truc};
        std::cout << "nb elem in vector : " << trucWrite.nbParam () << std::endl;
    
        return 0;
    }

    Tests.h

    #ifndef TESTS_H
    #define TESTS_H
    
    #include <vector>
    #include <string>
    
    
    class tTruc
    {
        public:
            tTruc() = default;
        protected:
            std::vector<int> m_trucALaCon;
            std::vector<std::string> m_autreTrucALaCon;
    };
    
    class tTrucRead : public tTruc
    {
        public:
            tTrucRead () = delete;
            tTrucRead (tTruc const & truc, std::string const & fileName);
        private:
            std::string m_fileName;
    };
    
    class tTrucModif : public tTruc
    {
        public:
            tTrucModif () = delete;
            tTrucModif (tTruc const & truc);
            void add (int const sample);
            unsigned int nbParam () const;
    };
    
    class tTrucWrite : public tTruc
    {
        public:
            tTrucWrite () = delete;
            tTrucWrite (tTruc const & truc);
            unsigned int nbParam () const;
    };
    
    #endif // TESTS_H
    

    tests.CPP

    #include "tests.h"
    
    tTrucRead::tTrucRead (tTruc const & truc, std::string const & fileName) : tTruc {truc}, m_fileName {fileName}
    { // Plein de chose : lecture du fichier, ....
        m_trucALaCon.push_back (0);
    }
    
    tTrucModif::tTrucModif (tTruc const & truc) : tTruc {truc}
    {}
    
    void tTrucModif::add (int const sample)
    {
        m_trucALaCon.push_back (sample);
    }
    
    unsigned int tTrucModif::nbParam () const
    {
        return m_trucALaCon.size();
    }
    
    tTrucWrite::tTrucWrite (tTruc const & truc) : tTruc {truc}
    {}
    
    unsigned int tTrucWrite::nbParam () const
    {
        return m_trucALaCon.size();
    }
    
    • Partager sur Facebook
    • Partager sur Twitter
      3 août 2019 à 14:30:45

      Salut,

      En fait, cela va aller beaucoup plus loin que "simplement trois" classes, car tu simplifie énormément le problème :p

      L'idée, c'est de ne pas se contenter d'essayer de respecter le SRP ici, mais de veiller effectivement à respecter ... l'ensemble des cinq principes SOLID (et la loi de Déméter, tant qu'on y est) ;)

      Par exemple, ici, tu vas sans doute partir sur la notion d'AST (Abstract Syntax Tree) pour être en mesure d'analyser correctement le contenu du fichier, si bien que tu te retrouveras déjà avec "un certain nombre de classes" (faisant -- a priori -- partie d'une hiérarchie commune) rien que pour représenter les différents éléments de ce que l'on peut appeler une "expression".

      Bien sur, pour pouvoir créer ton AST, tu devras respecter scrupuleusement le LSP (le principe de substitution de Liskov), sous peine de "tout faire tomber en marche"

      Cet AST, il faudra bien que tu le maintienne en mémoire, vu qu'il te permettra de représenter l'ensemble du contenu de ton fichier, ce qui fera encore une classe de plus.  Et, bien sur, tu devras pouvoir créer les différents "tokens" qui composent l'AST, ce qui nécessitera sans doute l'utilisation d'une "fabrique", ce qui fait aussi encore une classe de plus

      Arrivé à ce stade, tu devrais pouvoir recréer le fichier d'origine "à l'identique", mais tu n'aurais pas encore -- et de loin -- fini le travail, car nous venons tout juste de ... passer la première étape (la lecture)

      Mais, comme tu travailleras "toujours" sur le contenu du même fichier, un petit coup de respect du DIP (inversion des dépendances) s'avèrera indispensable, car, à partir d'ici, tu peux te dire que tout devras disposer de ton AST (et être capable de le parcourir ;) )

      Après, il faudra encore s'assurer que le contenu du fichier est valide, et là, on se rend compte que, dans une expression donnée, chaque token a une incidence très forte sur ... le token suivant.

      Pour prendre un exemple tout bête : tu ne peux pas mettre le mot clé statique et le mot clé virtual l'un à coté de l'autre.  Ce n'est donc pas une fonction, ni même une seule classe qui te permettront de faire toutes les vérifications possibles, mais... "un certain nombre" d'entre elles.

      Après, tu te rendras compte que certains éléments présentent la notion de "priorité" par rapport à d'autres : il n'y a -- par exemple -- qu'un moyen correct d'évaluer l'expression 3*5+4 dans le sens où la multiplication a priorité sur l'addition, si bien que tu dois normalement calculer 3*5 (15) et ajoutre 4 à ce résultat.  Sauf si tu commence à jouer avec les parenthèses, qui ont une priorité supérieure:

      Cette expression revient exactement au même que si tu l'avait écrite sous la forme de (3*5)+4, mais tu obtiendras un résultat différent si tu venais à écrire 3*(5+4)

      Par la suite, tu voudras modifier les données que tu as obtenues, mais, là encore, tu devras respecter certaines règles : Si on reprend l'expression 3*5+4, tu peux remplacer les trois tokens "3", "*" et "5" par un token "unique" de valeur "15" que tu pourrais utiliser pour la suite (donc, avec les tokens "+" et "4" ) pour obtenir un token de valeur "19".  Et, la notion de simplification risque aussi de demander "un certain nombre" de classes et de fonctions ;).  Tu ne pourras pas modifier le contenu du fichier "n'importe comment" (un exemple tout bête : quelle serait la valeur numérique équivalent à "hello world" ???)

      N'oublions pas l'OCP (le principe ouvert fermé) et l'ISP (ségrégation des interfaces) qui sera à eux seuls responsables de l'ajout d'un grand nombre de classes et / ou d'interfaces pour justement permettre les évolutions futures en limitant les modifications que nous devrons apporter au code existant ;)

      Il faut bien comprendre que si le SRP est sans doute le premier principe que l'on martèle aux gens, c'est uniquement parce que c'est le principe que les gens ont le plus facilement tendance à enfreindre, mais que les cinq principes (à l'exception peut-être du LSP, qui est spécifique à l'approche orientée objet) forment malgré tout "un tout" tellement ils s'emboitent les un dans les autres !

      • Partager sur Facebook
      • Partager sur Twitter
      Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
        4 août 2019 à 9:46:52

        Bonjour et merci pour ta réponse Koala01.

        [Je ne pensais pas recevoir de réponse hier! Je suis surpris ce matin de voir ton long texte]

        Merci pour ces explications, qui me rappel à la réalité: Bien sûr, il y aura sûrement plein de class dans ce projet, et pas uniquement 3 ! Bien sûr dans SOLID, il y a O, L, I, D (et Démeter en plus), à appliquer en même temps. En fait, actuellement j'en suis à l'architecture et l'étude de faisabilité, .... et je ne sais pas faire ! Alors au lieux de partir à l'envers, je vous demande conseille.

        Pour l'instant, ma difficulté est : si j'ai bien compris SRP, il faut que je crée 3 class pour réaliser les 3 responsabilités de l’outil. Or ces 3 class sont basées sur des données communes. Comment je fais pour passer les données d'une class à la suivante ? J'ai essayé de faire de l'héritage (les sources précédents), mais ça ne marche pas (Bug ou erreur de design ?).

        Merci d’avance de lire ce post, et merci si vous pouvez m’aider.

        Edit: J'ai trouvé une solution (qui ne me convient pas complètement .... mais "Faute de grive, on mange des merles!").

        Au lieux de faire hériter mes class d'une class de basse, j'ai inclus comme attribue membre cette class dans mes class "plus élaborées". Ca marche, ... Cependant, cette class de basse va se complexifier, car il faut que je mette dedans toutes les fonction membres pour accéder aux données, pour toutes les class "plus évoluées" qui vont l'utilisée. Dans le sources ci après, ma class tTruc à maintenant des fonctions membres, et il y en aura de plus en plus, en fonctions des fonctions à implémenter dans les "class évoluées".

        Note: ci-après, j'ai simplifié encore mon sources, --> réduit à 2 class, si je sais faire avec 2 class, je sais faire avec 3 !

        Merci pour vos conseils.

        Main.CPP

        #include <iostream>
        #include "tests.h"
        
        int main()
        {
            // 1) lecture fichier/modification
            tTrucRead trucRead;
            trucRead.add (1);
            trucRead.add (2);
            // 2) Sauvegarde ... enfin ici, seulement verification que cà marche
            tTrucWrite trucWrite {trucRead.getTruc()};
            std::cout << "nb elem in vector : " << trucWrite.nbParam () << std::endl;
        
            return 0;
        }

        tests.H

        #ifndef TESTS_H
        #define TESTS_H
        
        #include <vector>
        
        
        class tTruc
        {
            public:
                tTruc() = default;
                void addTrucALaCon (int const i);
                unsigned int nbTrucALaCon () const;
            protected:
                std::vector<int> m_trucALaCon;
        };
        
        class tTrucRead
        {
            public:
                tTrucRead () = default;
                void add (int const sample);
                tTruc getTruc () const;
            private:
                tTruc m_truc;
        };
        
        class tTrucWrite
        {
            public:
                tTrucWrite () = delete;
                tTrucWrite (tTruc const& truc);
                unsigned int nbParam () const;
            private:
                tTruc m_truc;
        };
        
        #endif // TESTS_H

        tests.CPP

        #include "tests.h"
        
        void tTruc::addTrucALaCon (int const i)
        {
            m_trucALaCon.push_back (i);
        }
        
        unsigned int tTruc::nbTrucALaCon () const
        {
            return  m_trucALaCon.size();
        }
        
        /**********************************************/
        void tTrucRead::add (int const sample)
        {
             m_truc.addTrucALaCon (sample);
        }
        
        tTruc tTrucRead::getTruc () const
        {
            return (m_truc);
        }
        
        /**********************************************/
        tTrucWrite::tTrucWrite (tTruc const& truc)
        {
            m_truc = truc;
        }
        
        unsigned int tTrucWrite::nbParam () const
        {
            return  m_truc.nbTrucALaCon ();
        }
        

        -
        Edité par Dedeun 4 août 2019 à 11:17:12

        • Partager sur Facebook
        • Partager sur Twitter
          4 août 2019 à 12:59:27

          Honnêtement pour la modélisation d'un AST, t'emmerdes pas avec une hiérarchie de classe, UN AST ça fournit pas de service, c'est juste un tas de données. Un AST c'est une SDD qui évolue très peu, il y a très rarement de nouveaux noeuds dans un AST. Il est beaucoup plus probable que tu aies souvent de nouveaux traitements.

          Alors go std::variant et en voiture Simone. Si dans le monde fonctionnel, on utilise des types sommes pour modéliser des AST, c'est pas juste un lubie, c'est parce que ça fait gagner beaucoup de temps.

          -
          Edité par Ksass`Peuk 4 août 2019 à 13:05:55

          • Partager sur Facebook
          • Partager sur Twitter

          Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

            4 août 2019 à 13:39:46

            Dedeun a écrit:

            Pour l'instant, ma difficulté est : si j'ai bien compris SRP, il faut que je crée 3 class pour réaliser les 3 responsabilités de l’outil. Or ces 3 class sont basées sur des données communes. Comment je fais pour passer les données d'une class à la suivante ? J'ai essayé de faire de l'héritage (les sources précédents), mais ça ne marche pas (Bug ou erreur de design ?).

            Justement, vu que les données sont communes, il faut au minimum une quatrième classe qui permettra de "gérer les données" (une "collection de donnée") qui exposera "tout ce qu'il faut" pour ajouter, retirer, et accéder (en lecture et en écriture) à ces données ;).

            Et il faudra veiller à transmettre cette collection de donnée sous forme de référence (constante ou non) aux éléments qui devront la manipuler ;)

            Après, la manière dont cette collection est transmises aux différentes classes peut varier énormément, dans le sens où tu peux créer une donnée membre de la classe (sous forme de référence, constante ou non), sous une forme qui serait proche de

            /* faisons simple : les données sont gérées par un tableau
             *  de chaines de caractères
             */
            using AllDatas_t = std::vector<std::string>;
            /* pour lire le contenu du fichier */
            class Reader{
            public:
                Reader(Reader const &) = delete;
                Reader & operator=(Reader const &) = delete;
                /* Parce que l'ouverture du fichier peut se faire
                 * différemment selon le type de fichier à lire
                 * alors... la lecture elle-même !!!
                 */
                virtual void readFile(std::string const & filename) = 0;
            protected:
                Reader(AllDatas_t & datas):datas_{datas}{
                }
                ~Reader()  = default;
                AllDatas_t & datas(){
                    return datas_;
                }
            private:
                AllDatas_t & datas_;
            };
            /* utilisé sous une forme proche de
             */
            int main(){
                AllDatas_t datas;
                /* Une des classes dérivée de Reader permet la lecture
                 * de fichiers XML
                 */ 
                XmlReader reader{datas};
                reader.readFile("Fichier.xml");
            }

            ou tu peux aussi  décider de transmettre cette collection  de données aux fonctions "qui en ont besoin" (en gros : toutes, mais bon...) sous une forme proche de

            /* nous utilisons toujours AllDatas_t */
            class Checker{
            public:
                Checker(Checker const &) = delete;
                Checker & operator=(Checker const &) = delete;
                /* parce que les checks varient en fonction du
                 * type de fichier dont elles sont issues
                 */
                void checkDatas(AllDatas_t const & datas) = 0;
            protected:
                Checker() = default;
                ~Checker() = default;
            }
            /* utilisé sous une forme qui serait proche de
             */
            int main(){
                AllDatas_t datas;
                /* les données sont extraites du fichier, puis
                 * il faut les vérifier
                 * On a une classe qui s'occupe de vérifier les
                 * la structure des fichier XML
                 */
                XmlChecker checker;
                checker.check(datas);
                /* après, faudra vérifier si ces données respectent 
                 * le schéma XML imposé 
                 */
                
            }

            Dedeun a écrit:

            Au lieux de faire hériter mes class d'une class de basse, j'ai inclus comme attribue membre cette class dans mes class "plus élaborées". Ca marche, ... Cependant, cette class de basse va se complexifier, car il faut que je mette dedans toutes les fonction membres pour accéder aux données, pour toutes les class "plus évoluées" qui vont l'utilisée. 

            Et l'ISP, tu en fais quoi ????

            Tu dois veiller à garder les interfaces de tes classes les plus simple possibles, ce qui fait que tu ne peux pas t'amuser à faire des "classes plus élaborées" ;)

            note au passage que la très grosse majorité des fonctions de XmlReader et de XmlChecker (dans mes exemples) seront dans l'accessibilité privée et non virtuelles, justement pour garder l'interface la plus simple possible ;)

            Dedeun a écrit:

            Note: ci-après, j'ai simplifié encore mon sources, --> réduit à 2 class, si je sais faire avec 2 class, je sais faire avec 3 !

            Non, justement!!! Si tu en viens à penser que tu pourrais faire en deux classes ce qui en aurait normalement (d'après ta première analyse) nécessité trois, poses toi la question de savoir s'il ne faut pas -- au contraire -- envisager d'en créer une quatirème ;)

            C'est plutôt contre intuitif dit de cette manière, n'est-ce pas?  Et pourtant : si tu prévois de "supprimer" une classe, cela implique que la responsabilité que cette classe prenait devra ... être partagée par une ou plusieurs des classes restantes, ce qui produira des responsabilités plus complexes.

            Du coup, il est peut-être utile de se demander si toutes les responsabilités que l'on a définies le sont correctement, s'il n'existe pas une responsabilité qui soit déjà "trop vaguement définie", et que l'on pourrait scinder en deux responsabilités (ou plus) plus "précises".  Et chacune de ces responsabilités "plus précises" mériterait amplement d'être prise en charge par ... une classe (ou un type de donnée, pourquoi devrait-on se limiter aux classes ?? :D ) bien particulière

            • Partager sur Facebook
            • Partager sur Twitter
            Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
              5 août 2019 à 21:27:59

              Merci Koala01, merci Ksass`Peuk,

              J'ai quelques éléments de réponse à ma question, [Ca c’est le bon coté] et beaucoup de nouvelles questions [Ca c’est moins cool ! Mais comme vous êtes sympa, je viendrais les poser quand nécessaires !]

              1) Donc, pas d’héritage, mais inclusion d’une class « de base », passée par référence (Merci pour l’exemple Koala01, j’ai réussis à le compléter pour que ça compile, et que ça tests ce que je voulais voir).

              2) AST : Je ne sais pas encore comment est le format du fichier de définition, Je verrai plus tard, chaque chose en son temps (mais il faut que je garde la possibilité d’avoir à interpréteur de XML, mais aussi de Ini, du CSV, ...). Dans l’architecture, il y aura sûrement plusieurs class de reader (XmlReader, IniReader, CsvReader, ...), qui dériverons certainement d’une class mère Reader, et qui seront instanciées en fonction de l’extension du nom du fichier. On verra plus tard.

              3)

              Koala01 a écrit:

              Et l'ISP, tu en fais quoi ????

              Tu dois veiller à garder les interfaces de tes classes les plus simple possibles, ce qui fait que tu ne peux pas t'amuser à faire des "classes plus élaborées" ;)

              Ok, c’est bien ce que j’ai peur. En effet, si mes 3 class évoluées « Reader », « Checker » et « Writer » ont pour membre une class « de base », il faut que cette class « de base » propose les fonctions membres pour accéder au données de cette class « de base ». Je m’explique :

              *La class « de base » contiendra une liste,

              *la class « Reader » aura besoin d’accéder à la liste pour ajouter un élément,

              *la class « Checker » aura besoin d’accéder à la liste pour vérifier chaque élément,

              *La classe « Checker » aura besoin d’accéder à la liste pour supprimer les doublons,

              *La class « Writer » aura besoin d’accéder à la liste pour lire chaque élément, dans l’ordre.

              Donc, outre la liste comme donnée membre, il faudra que la class « de base » fournisse aussi 4 fonctions membres, pour ajouter, pour vérifier, pour supprimer les doublons, pour lire.

              Ca fait beaucoup, Non ? C’est encore simple ?

              Et puis, quelle est la « responsabilité » de cette class de base ?

              Mais enfin, on va voir ce que ça donne … Il faut que je continue, pour voir ce que ça donne.

              4)

              koala01 a écrit:

              Dedeun a écrit:

              Note: ci-après, j'ai simplifié encore mon sources, --> réduit à 2 class, si je sais faire avec 2 class, je sais faire avec 3 !

              Non, justement!!! Si tu en viens à penser que tu pourrais faire en deux classes ce qui en aurait normalement (d'après ta première analyse) nécessité trois, poses toi la question de savoir s'il ne faut pas -- au contraire -- envisager d'en créer une quatirème ;)

              Stop ! Stop ! On ne se comprend plus ! J’ai réduit à 2 class … dans l’exemple (les sources que j’ai affiché), pas pour le projet, là il y en aura d’autres class …

              Ayant réponse à ma première question, je clos le sujet… mais je serai sûrement de retour, avec plein d’autres questions …

              Merci encore pour vos réponses.

              -
              Edité par Dedeun 5 août 2019 à 21:31:13

              • Partager sur Facebook
              • Partager sur Twitter
                7 août 2019 à 1:08:48

                Dedeun a écrit:

                Stop ! Stop ! On ne se comprend plus ! J’ai réduit à 2 class … dans l’exemple (les sources que j’ai affiché), pas pour le projet, là il y en aura d’autres class …

                J'espère bien :D

                En fait, on pourrait résumer le SRP en une seule règle toute simple : toutes les notions qui apparaissent dans ton analyse des besoins / analyse fonctionnelles doivent apparaitre -- d'une manière ou d'une autre -- dans ton code.

                Le meilleur moyen pour appliquer cette règle est encore de partir du principe que

                • chaque nom (ou groupe nominal) présent dans ton analyse fonctionnelle devra être traduit par un nom de (type de ) donnée  dans ton code
                • chaque verbe(ou groupte verbal) présent dans ton analyse fonctionnelle devra être traduit par une fonction dans ton code

                Si l'on reprend ton exemple, tu souhaite obtenir une application qui puisse

                1. charger le contenu d'un fichier (dont le nom est fourni)
                2. extraire les données correspondantes, sur base d'un format particulier (celui du fichier, en fait)
                3. vérifier la cohérence des données
                4. (éventuellement) analyser les données afin d'en déduire les modifications qu'il faudrait y apporter
                5. (éventuellement)  appliquer les modifications définies en (4) au données extraites en (3)
                6. (éventuellement) sauvegarder les données dans un fichier (qui peut être celui d'origine ... ou non) dans un format donné (qui peut être le format d'origine ... ou non

                (les termes importants sont affichés en gras ;) )

                Tu devras donc au minimum avoir six fonctions (une pour chaque groupe verbal) et ... quatre types de données répartis comme suit :

                FileContent contant = load("filename.ext");
                Datas datas = extract(content, FormatXYZ{});
                if(! check(datas) ){
                    throw std::runtime_error("données incohérentes");
                }
                /* éventuellement */
                Changes changes = analyse(datas);
                /* éventuellement */
                if(!changes.empty())
                    apply(changes, datas);
                /* éventuellement */
                save(datas, formatABC{}/*, "filename.exz" */);

                A partir de là, il y aura quelques aspects qui devront être peaufinés, car:

                Comment va-t-on déterminer le format utilisé pour l'extraction :question: Cela peut être un format "par défaut", mais une solution est aussi de se baser directement sur l'extension du fichier qu'on lit (si cette extension est cohérente).

                Il faudrait donc sans doute ajouter (entre le (1) et le (2)

                • déterminer le format d'extraction sur base de l'extension du fichier

                Ce qui se traduirait dans le code par

                Format format=determineFormat("filename.ext");

                Et, si on ne veut pas d'un format "par défaut", et qu'on craint de ne pas faire confiance à l'extension du fichier, il faudra peut-être permettre à l'utilisateur de l'application de l'indiquer directement dans les options de l'applicaiton.  Mais, du coup, comment faudra-t-il réagir si il y a inconsistance entre le format représenté par l'extension du fichier et l'option indiquée par l'utilisateur ???

                Ca, c'est typiquement le genre de choses que seul le concepteur de l'application peut décider ;)

                Dans le même genre d'idée, si l'utilisateur décide de sauvegarder les données, comment faudra-t-il traiter le nom de fichier potentiel et / ou le format de sortie potentiel (par opposition au nom de fichier lu et au format d'entrée, s'entend) ???

                Or, les réponses à ces questions vont -- forcément -- faire apparaitre de nouvelles notions dans ton analyse des besoins.  Et donc, t'obliger à rajouter de nouvelles notions (sous forme de données, de types de données et / ou de fonctions) dans ton code ;)

                • Partager sur Facebook
                • Partager sur Twitter
                Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                  6 septembre 2019 à 18:53:32

                  Juste une remarque à 2 balles, pour de la "conversion", il existe pléthores de langages (XSLT, etc...) et d'outils (tous les ETL, ...) qui feront très largement l'affaire et cela sera bien plus flexible et maintenable qu'un code C++ dans un coin.
                  • Partager sur Facebook
                  • Partager sur Twitter
                  Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                    7 septembre 2019 à 10:04:44

                    Bonjour Bacelar,

                    Merci de me donner une nouvelle vision à ma demande. J'en suis vraiment très surpris ... et je ne sais pas bien quoi en penser! Mais en tout cas, ça m'ouvre des idées! Merci;

                    En effet wikipedia me dit que :

                    .... le langage XSLT permet aussi les transformations vers tout autre type de document, au format texte ou dans un format binaire (bien que ceci ne soit pas nativement prévu par la recommandation XSLT). ...

                    Pour les ELT (Extract Load Transform), j'ai lu une présentation internet et ça ne me semble pas approprier (la BI ! SAP and co ....) ça me semble "l'usine à gaz" pour mon petit besoin .... (Mes fichiers de sortie fond quelques KOctets !)

                    Si je décrit un peut plus mon besoin: le format de sortie -> binaire pure, incluant des pointeurs (pour trouver le début des champs), les entiers sur 16, 32 ou 64 bits (il y a les 3 formats) qui se suivent (c'est leur positions qui donne leur sens), des chaînes de caractère sous un format du style ASN1 (longueur/donnée),, des conditions entre les différents champs (a chaque fois différentes)... Bien sûr ce format est l'interface entre différents "provider" (concurrent) et il est normalisé (pas de déviation possible) ! C'est ce que j’appelle un format "alacon".

                    Mon idée était de m’exercer au C++  (j'suis un pöve débutant en POO!), plutôt que de développer en C, comme actuellement .... Mais les vacances des chefs sont finies, le travaille "classique" a repris !

                    En tout cas merci pour cette nouvelle réponse.

                    • Partager sur Facebook
                    • Partager sur Twitter
                      9 septembre 2019 à 10:44:19

                      En ETL, il y a de gros acteurs mais le nombre de solution est pléthoriques et souvent très "light".

                      Le niveau de customisation est très important et très souvent avec des langages ou des framework adaptés à ce type d'action "à la con".

                      Le caractère "visuel" de ce type d'outil simplifie grandement la maintenance de ces "moulinettes", voire passer le bébé aux "fonctionnels".

                      Il faut toujours penser à l'outil le plus adapté à la tâche à effectuer avant de se prendre la tête avec une conception.

                      • Partager sur Facebook
                      • Partager sur Twitter
                      Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.

                      Le « S » de « SOLID »

                      × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                      × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                      • Editeur
                      • Markdown