+ Répondre à la discussion Actualité déjà publiée
  1. #1
    Responsable Qt


    Avatar de dourouc05
    Homme Profil pro
    Ingénieur de recherches
    Inscrit en
    août 2008
    Messages
    22 401
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur de recherches
    Secteur : Enseignement

    Informations forums :
    Inscription : août 2008
    Messages : 22 401
    Points : 122 588
    Points
    122 588

    Par défaut [InjeQt] Sortie d'InjeQt 1.1

    L’injection de dépendances est un patron de conception qui a de plus en plus la cote. Un de ses objectifs est d’éviter l’utilisation de singletons dans une application (qui provoquent les mêmes troubles que des variables globales) en implémentant l’inversion de contrôle : au lieu que le « code spécifique » (comme une manière de lire des données : du réseau, d’un fichier, etc.) appelle le « code générique » (par exemple, un traitement sur des données), c’est l’inverse qui se produit. Ce « code générique » exploite donc des interfaces pour appeler le « code spécifique » qui lui est fourni en argument. Cette manière de concevoir des applications permet notamment de faciliter les tests : la partie injectée peut être implémentée au niveau du test, ce qui permet de tester exclusivement le « code générique ».

    InjeQt est une implémentation de ce patron de conception pour Qt. Ce fait n’est pas si anodin : d’habitude, ces implémentations sont font dans des langages plus dynamiques que le C++, en exploitant des fonctionnalités de réflexion… qui n’existent pas en C++. Par contre, Qt les fournit par son système de métaobjets. InjeQt peut donc proposer des services (du « code spécifique »), pour peu qu’il dérive de QObject (directement ou non). Les dépendances entre classes sont alors explicitées par le biais de la macro INJEQT_SET.

    La version 1.1, nouvellement sortie, permet l’injection directe dans un objet par la méthode inject_into(QObject*), ainsi que la définition d’une hiérarchie d’injecteurs (composée de parents et de sous-injecteurs provider_by_parent_injector). Cette dernière fonctionnalité permet notamment à une extension d’une application de récupérer une série de services déjà définis à l’échelle globale (un sous-injecteur) depuis l’injecteur défini dans cette extension (l’injecteur parent).

    Télécharger le code source d’InjeQt 1.1.
    Source : Injeqt 1.1 and testing Qt applications et le code source d’InjeQt.
    Ce contenu a été publié dans Qt par dourouc05.
    Vous souhaitez participer aux rubriques Qt ou PyQt (tutoriels, FAQ, traductions) ? Contactez-moi par MP.

    Nouveau ! Créer des applications graphiques en Python avec PyQt5
    Créer des applications avec Qt 5.

    Pas de question d'ordre technique par MP !

  2. #2
    Membre chevronné
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    avril 2016
    Messages
    461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : avril 2016
    Messages : 461
    Points : 2 036
    Points
    2 036

    Par défaut

    Question naïve : Mais à quoi ça sert, ce truc ?

    Pour comprendre l'injection de dépendances, j'ai lu plusieurs articles Java.
    Aujourd'hui, le dernier que j'ai lu, qui était le plus complet sur l'injection de dépendances, était celui-ci : https://martinfowler.com/articles/injection.html
    Mais je ne suis toujours pas convaincu par l'utilité d'utiliser un framework d'injection de dépendances, quel que soit le projet.

    Dans le code source de injeqt, j'ai lu un exemple de "hello world" :
    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
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    #include <injeqt/injector.h>
    #include <injeqt/module.h>
     
    #include <QtCore/QObject>
    #include <iostream>
    #include <memory>
    #include <string>
     
    class hello_service : public QObject
    {
    	Q_OBJECT
     
    public:
    	hello_service() {}
    	virtual ~hello_service() {}
     
    	std::string say_hello() const
    	{
    		return {"Hello"};
    	}
    };
     
    class world_service : public QObject
    {
    	Q_OBJECT
     
    public:
    	world_service() {}
    	virtual ~world_service() {}
     
    	std::string say_world() const
    	{
    		return {"World"};
    	}
    };
     
    class hello_factory : public QObject
    {
    	Q_OBJECT
     
    public:
    	Q_INVOKABLE hello_factory() {}
    	virtual ~hello_factory() {}
     
    	Q_INVOKABLE hello_service * create_service()
    	{
    		return new hello_service{};
    	}
    };
     
    class hello_client : public QObject
    {
    	Q_OBJECT
     
    public:
    	Q_INVOKABLE hello_client() : _s{nullptr}, _w{nullptr} {}
    	virtual ~hello_client() {}
     
    	std::string say() const
    	{
    		return _s->say_hello() + " " + _w->say_world() + "!";
    	}
     
    private slots:
    	INJEQT_INIT void init()
    	{
    		std::cerr << "all services set" << std::endl;
    	}
     
    	INJEQT_DONE void done()
    	{
    		std::cerr << "ready for destruction" << std::endl;
    	}
     
    	INJEQT_SET void set_hello_service(hello_service *s)
    	{
    		_s = s;
    	}
     
    	INJEQT_SET void set_world_service(world_service *w)
    	{
    		_w = w;
    	}
     
    private:
    	hello_service *_s;
    	world_service *_w;
     
    };
     
    class module : public injeqt::module
    {
    public:
    	explicit module()
    	{
    		_w = std::unique_ptr<world_service>{new world_service{}};
     
    		add_type<hello_client>();
    		add_type<hello_factory>();
    		add_factory<hello_service, hello_factory>();
    		add_ready_object<world_service>(_w.get());
    	}
     
    	virtual ~module() {}
     
    private:
    	std::unique_ptr<world_service> _w;
     
    };
     
    int main()
    {
    	auto modules = std::vector<std::unique_ptr<injeqt::module>>{};
    	modules.emplace_back(std::unique_ptr<injeqt::module>{new module{}});
     
    	auto injector = injeqt::injector{std::move(modules)};
    	auto client = injector.get<hello_client>();
    	auto hello = client->say();
     
    	std::cout << hello << std::endl;
    }
     
    #include "hello-world.moc"
    Bien sûr, ce n'est qu'un "hello world" pour aider à la compréhension, mais faisons comme si toutes les classes présentes étaient suffisamment compliquées pour justifier leur découpage.

    Dans la suite de mon message, pour faciliter la compréhension, je vais décomposer hello_service en deux classes : une classe abstraite hello_service_base et une classe concrète qui en dérive, hello_service_impl. De même, je vais décomposer world_service en world_service_base et world_service_impl.

    Alors, pour avoir au moins autant d'indirections que dans l'exemple qui utilise injeqt, j'aurais codé ainsi :
    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
    #include <iostream>
    #include <memory>
    #include <string>
     
    class hello_service_base
    {
    public:
    	hello_service_base() {}
    	virtual ~hello_service_base() {}
    	virtual std::string say_hello() const = 0;
    };
     
    class hello_service_impl final : public hello_service_base
    {
    public:
    	std::string say_hello() const final {return "Hello";}
    };
     
    class world_service_base
    {
    public:
    	world_service_base() {}
    	virtual ~world_service_base() {}
    	virtual std::string say_world() const = 0;
    };
     
    class world_service_impl final : public world_service_base
    {
    public:
    	std::string say_world() const final {return "World";}
    };
     
    //! Rappel de l'intérêt d'une fabrique :
    //! On a une indirection sur la création d'un objet hello_service_base.
    //! Un jour, on pourrait changer l'implémentation de create_service()
    //! pour retourner un objet d'un autre type que hello_service_impl.
    class hello_factory final
    {
    public:
    	static hello_factory& instance() {
    		static hello_factory soleInstance;
    		return soleInstance;
    	}
    	std::unique_ptr<hello_service_base> create_service() const
    	{
    		return std::make_unique<hello_service_impl>();
    	}
    private:
    	~hello_factory() {}
    };
     
    class hello_client final
    {
    public:
    	hello_client(hello_service_base& h, world_service_base& w) :
    		m_hello_service(h),
    		m_world_service(w)
    	{}
    	std::string say() const
    	{
    		return m_hello_service.say_hello() + " " + m_world_service.say_world() + "!";
    	}
    private:
    	hello_service_base& m_hello_service;
    	world_service_base& m_world_service;
    };
     
    //! Indirection sur la construction des objets qui dérivent de hello_service_base et de world_service_base.
    //! Pour faire une analogie avec l'injection de dépendances dans Spring de Java,
    //! la classe présente a le même rôle que le fichier XML qui contient les "beans".
    class configuration final
    {
    public:
    	static configuration& instance() {
    		static configuration soleInstance;
    		return soleInstance;
    	}
    	hello_service_base& get_hello_service() {return *m_hello_service;}
    	world_service_base& get_world_service() {return m_world_service;}
    private:
    	configuration() :
    		m_hello_service{hello_factory::instance().create_service()},
    		m_world_service{}
    	{}
    	std::unique_ptr<hello_service_base> m_hello_service;
    	world_service_impl                  m_world_service;
    };
     
    int main()
    {
    	hello_service_base& hello_service = configuration::instance().get_hello_service();
    	world_service_base& world_service = configuration::instance().get_world_service();
    	hello_client client{hello_service, world_service};
    	std::cout << client.say() << std::endl;
    }
    Certains développeurs Java disent que, grâce à l'injection de dépendances, on peut facilement découpler. En général, ils donnent un exemple qui ressemble à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class hello_client final
    {
    public:
    	std::string say() const
    	{
    		return m_hello_service.say_hello() + " " + m_world_service.say_world() + "!";
    	}
    private:
    	hello_service_impl m_hello_service;
    	world_service_impl m_world_service;
    };
    puis ils disent que, avec l'injection de dépendances, on peut découpler hello_client de hello_service_impl et de world_service_impl. Ils remplacent alors le code ci-dessus par un code qui ressemble au code ci-dessous :
    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
    class hello_client final
    {
    public:
    	hello_client(hello_service_base& h, world_service_base& w) :
    		m_hello_service(h),
    		m_world_service(w)
    	{}
    	std::string say() const
    	{
    		return m_hello_service.say_hello() + " " + m_world_service.say_world() + "!";
    	}
    private:
    	hello_service_base& m_hello_service;
    	world_service_base& m_world_service;
    };
    Mais, pour ça, il n'y a besoin d'aucun framework. Il s'agit simplement du patron de conception Stratégie.

    Les développeurs Java disent aussi que, avec l'injection de dépendances, on peut configurer les objets utilisés.
    En général, il s'agit d'un fichier de configuration XML (voir les "beans" dans Spring de Java) ou d'un bout de code lié à un certain framework.
    Mais je ne vois pas l'utilité de passer par un framework. Il suffit d'écrire une classe comme la classe configuration de mon exemple.

Discussions similaires

  1. Réponses: 16
    Dernier message: 18/07/2003, 18h16
  2. Comment rediriger la sortie vers /dev/null
    Par dclink dans le forum C
    Réponses: 4
    Dernier message: 24/06/2003, 19h23
  3. [LG]Entrées et sorties redirigée
    Par Lady dans le forum Langage
    Réponses: 17
    Dernier message: 10/05/2003, 19h33
  4. Rediriger le plux de sortie
    Par Groove dans le forum C
    Réponses: 5
    Dernier message: 17/04/2003, 18h16
  5. récupérer la valeur de sortie d'un thread
    Par jakouz dans le forum Langage
    Réponses: 3
    Dernier message: 31/07/2002, 12h28

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo