Modèles de conception: usine abstraite vs méthode d'usine


141

Remarque: les questions se trouvent à la fin de l'article.

J'ai lu les autres threads de stackoverflow concernant Abstract Factory vs Factory Method . Je comprends l'intention de chaque modèle. Cependant, je ne suis pas clair sur la définition.

La méthode Factory définit une interface pour créer un objet, mais laisse les sous-classes décider lesquelles de celles à instancier. Une méthode de fabrique permet aux classes de différer l'instanciation aux sous-classes.

En revanche, une fabrique abstraite fournit une interface pour créer des familles d'objets liés ou dépendants sans spécifier leurs classes concrètes.

- John Feminella

L' usine abstraite ressemble beaucoup à la méthode d'usine . J'ai dessiné quelques classes UML pour illustrer mon propos.

Remarque:

  • Le diagramme provient de schémas www.yuml.com donc ils ne sont pas parfaitement orientés. Mais c'est un service gratuit :).
  • Les diagrammes peuvent ne pas être parfaits. J'apprends toujours les modèles de conception du GoF .

Méthode d'usine:

Méthode d'usine

Abstract Factory (seulement 1 membre):

Abstract Factory (seulement 1 membre)

Abstract Factory (plus de membres):

texte alternatif

Des questions:

  1. Si Abstract Factory n'a qu'un seul créateur et qu'un seul produit, s'agit-il toujours du modèle Abstract Factory ?(une interface pour créer des familles)
  2. Le créateur concret de la méthode d'usine peut-il être créé à partir d'une interface ou doit-il provenir d'une classe?(les classes reportent les instanciations aux sous-classes)
  3. Si la fabrique abstraite ne peut avoir qu'un seul créateur et un seul produit, la seule différence entre la fabrique abstraite et la méthode de fabrique est-elle que le créateur de la première est une interface et le créateur de la seconde est une classe?

1
Remarque: lorsque je fais référence à Interface, je pensais davantage aux termes d'une interface Java (classe abstraite avec des méthodes virtuelles abstraites). N'hésitez pas à clarifier s'il existe une différence entre la fabrique abstraite et la méthode Factory dans différentes langues.

Une différence fondamentale ici: stackoverflow.com/questions/1001767 , mais pas aussi spécifique que vous le demandez ..
nawfal

Réponses:


134

J'espère que cela t'aides. Il décrit les différents types d'usines. J'ai utilisé les modèles de conception Head First comme référence. J'ai utilisé yuml.me pour schématiser.

Usine statique

Est une classe avec une méthode statique pour produire divers sous-types de produit.

Usine statique

Usine simple

Est une classe qui peut produire différents sous-types de produit. (C'est mieux que la Static Factory. Lorsque de nouveaux types sont ajoutés, la classe Product de base n'a pas besoin d'être modifiée uniquement la classe Simple Factory)

Usine simple

Méthode d'usine

Contient une méthode pour produire un type de produit lié à son type. (C'est mieux qu'une usine simple car le type est reporté à une sous-classe.)

Méthode d'usine

Usine abstraite

Produit une famille de types liés. Elle est sensiblement différente d'une méthode d'usine car elle a plus d'une méthode de types qu'elle produit. (C'est compliqué, reportez-vous au diagramme suivant pour un meilleur exemple réel).

Usine abstraite

Exemple du .NET Framework

DbFactoriesProvider est une usine simple car il n'a pas de sous-types. Le DbFactoryProvider est une fabrique abstraite car il peut créer divers objets de base de données connexes tels que des objets de connexion et de commande.

Usine abstraite de .NET Framework


La différence entre Static Factory et Simple Factory est-elle simplement que la méthode CreateProduct se trouve dans une classe différente?
Peter O'Callaghan

4
Ne serait-il pas plus clair si, dans le cas de la méthode d'usine, il y avait juste Product(en tant qu'abstrait), puis Product1et Product2, en tant que fils? Cela aiderait à souligner que la méthode d'usine consiste simplement à créer un produit, alors que Abstract Factory est plus ou moins un ensemble de méthodes d'usine rassemblées en familles.
lllllll

79

Les deux modèles sont certainement liés!

La différence entre les modèles réside généralement dans l'intention.

L' intention de la méthode d'usine est de "définir une interface pour créer un objet, mais laisser les sous-classes décider quelle classe instancier. La méthode Factory permet à une classe de différer l'instanciation aux sous-classes."

L' intention de Abstract Factory est de «fournir une interface pour créer des familles d'objets liés ou dépendants sans spécifier leurs classes concrètes».

En me basant uniquement sur ces déclarations d'intention (citées par le GoF), je dirais qu'en effet la méthode Factory est en quelque sorte une fabrique abstraite "dégénérée" avec une famille d'un.

Ils ont généralement tendance à différer dans la mise en œuvre, car méthode Factory est beaucoup plus simple que Abstract Factory .

Cependant, ils sont également liés dans la mise en œuvre. Comme indiqué dans le livre du GoF,

AbstractFactory déclare uniquement une interface pour créer des produits. C'est aux sous-classes ConcreteProduct de les créer. La manière la plus courante de procéder consiste à définir une méthode d'usine pour chaque produit.

Ce wiki c2 a également quelques discussions intéressantes sur ce sujet.


7
Je ne comprends ni le commentaire ni le vote défavorable. Peux-tu élaborer?
Don Roby

Eh bien, les réponses me semblent rhétoriques ... Pas d'exemple concret réel ... trop large ...
Novalis

14

Il semble que la liste des (excellentes) questions du PO ait été ignorée. Les réponses actuelles offrent simplement des définitions remaniées. Je vais donc essayer de répondre de manière concise aux questions initiales.

  1. Si Abstract Factory n'a qu'un seul créateur et un seul produit, s'agit-il toujours du modèle Abstract Factory ? (une interface pour créer des familles)

Non . Une usine abstraite doit créer plus d'un produit pour créer une «famille de produits connexes». L'exemple canonique du GoF crée ScrollBar()et Window(). L'avantage (et le but) est que Abstract Factory peut appliquer un thème commun à ses multiples produits.

  1. Le créateur concret de la méthode d'usine peut-il être créé à partir d'une interface ou doit-il provenir d'une classe? (les classes reportent les instanciations aux sous-classes)

Tout d'abord, nous devons noter que ni Java ni C # n'existaient lorsque le GoF a écrit son livre. L'utilisation par le GoF du terme interface n'est pas liée aux types d'interface introduits par des langages particuliers. Par conséquent, le créateur concret peut être créé à partir de n'importe quelle API. Le point important dans le modèle est que l'API utilise sa propre méthode d'usine, de sorte qu'une interface avec une seule méthode ne peut pas être une méthode d'usine plus qu'elle ne peut être une usine abstraite.

  1. Si la fabrique abstraite ne peut avoir qu'un seul créateur et un seul produit, la seule différence entre la fabrique abstraite et la méthode de fabrique est-elle que le créateur de la première est une interface et le créateur de la seconde est une classe?

Cette question n'est plus valable, suite aux réponses ci-dessus; cependant, si vous pensez que la seule différence entre Abstract Factory et Factory Method est le nombre de produits créés, considérez comment un client consomme chacun de ces modèles. Une fabrique abstraite est généralement injectée dans son client et appelée via composition / délégation. Une méthode d'usine doit être héritée. Donc, tout revient à l'ancien débat composition vs héritage.

Mais ces réponses ont soulevé une quatrième question!

  1. Puisque, une interface avec une seule méthode ne peut pas être une méthode d'usine, pas plus qu'elle ne peut être une usine abstraite , comment appelle-t-on une interface créative avec une seule méthode?

Si la méthode est statique, elle est communément appelée Static Factory . Si la méthode n'est pas statique, elle est communément appelée usine simple . Ni l'un ni l'autre n'est un modèle GoF, mais dans la pratique, ils sont beaucoup plus couramment utilisés!


1
À propos de la composition et de l'héritage, je me suis toujours demandé: n'est-il pas possible de faire également de la composition avec le motif de la méthode d'usine? Qu'est-ce qui empêcherait de composer ou d'injecter la bonne usine de béton chez un client? Ou est-ce quelque chose qui sort déjà du cadre du modèle?
georaldc

1
@georaldc, du GoF (page 107) "La méthode d'usine permet à une classe de différer l'instanciation aux sous-classes. " En d'autres termes, la méthode d'usine utilise l'héritage par définition.
jaco0646

4

À mon avis, la légère différence entre les deux modèles réside dans l' applicabilité , et donc, comme déjà dit, dans l' intention .

Récapitulons les définitions (toutes deux tirées de Wikipedia).

Usine abstraite

Fournit une interface pour créer des familles d'objets liés ou dépendants sans spécifier leurs classes concrètes.

Méthode d'usine

Définissez une interface pour créer un objet , mais laissez les classes qui implémentent l'interface décider quelle classe instancier . La méthode Factory permet à une classe de différer l'instanciation aux sous-classes.

Les deux modèles permettent de découpler les objets utilisateur de la création des instances nécessaires (découplage à l'exécution), et c'est l'aspect commun. Les deux modèles permettent de créer une hiérarchie d'usines en fonction des besoins spécifiques, et c'est un autre aspect commun.

Abstract Factory permet de créer plusieurs types d'instances différents dans une sous-classe, et de particulariser le comportement des créations dans ses différentes sous-classes; normalement, la méthode Factory déclare la création d'un seul type d'objet qui peut être particularisé selon le mécanisme de sous-classification. Voilà la différence.

En résumant. Disons que Product définit la super-classe des objets de création, et que ProductA et ProductB sont deux sous-classes différentes. Ainsi, la méthode Abstract Factory aura deux méthodes, createProductA () et createProductB () qui seront particularisées (en termes d'étapes de création) dans ses sous-classes spécifiques: les sous-classes de fabrique particularisent les étapes de création pour les deux classes définies d'objets en cours de création.

Selon l'exemple ci-dessus, la méthode Factory sera implémentée différemment, en faisant abstraction de la création de ProductA et ProductB dans autant d'usines (une méthode par Factory), et la spécialisation supplémentaire des étapes de création sera déléguée à la hiérarchie au fur et à mesure de sa construction. .


2

Si j'ai créé une classe d' usine abstraite (référencée via une interface ou une classe de base abstraite) qui crée des objets qui n'ont qu'une seule méthode pour créer des objets, alors ce serait une méthode d'usine .

Si la fabrique abstraite avait plus d'une méthode pour créer des objets, alors ce serait une fabrique abstraite .

Disons que je crée un Manager qui gérera les besoins des méthodes d'action pour un contrôleur MVC. S'il avait une méthode, par exemple pour créer les objets moteur qui seront utilisés pour créer des modèles de vue, alors ce serait un modèle de méthode d'usine. D'un autre côté, s'il avait deux méthodes: une pour créer des moteurs de modèle de vue et une autre pour créer des moteurs de modèle d'action (ou tout ce que vous voulez appeler le modèle que la méthode d'action contient des consommateurs), alors ce serait une fabrique abstraite.

public ActionResult DoSomething(SpecificActionModel model)
{
    var actionModelEngine = manager.GetActionModelEngine<SpecificActionModel>();
    actionModelEngine.Execute(SpecificActionModelEnum.Value);

    var viewModelEngine = manager.GetViewModelEngine<SpecificViewModel>();
    return View(viewModelEngine.GetViewModel(SpecificViewModelEnum.Value);
}

1

Bien que cela fasse de nombreuses années que les gens de StackOverflow se sont interrogés sur ce problème de la même manière dans d'autres articles (le plus ancien remonte à 2009), je n'ai toujours pas trouvé la réponse que je voulais.


J'ai donc fait quelques heures de recherche sur le Web, passé en revue les exemples, et suis arrivé à cette conclusion, les principales différences entre Abstract Factory et Factory Method sont

  • L'intention: cohérence ou "look-and-feel" : l'intention de Abstract Factory est de regrouper une famille d'objets avec un même style (ex. Mêmes widgets UI look-and-feel, pièces automobiles de même style, objets d'un même OS, etc.) De nombreux exemples de Abstract Factory mentionnent la phrase clé "le même look-and-feel".
  • Objets formant un objet de groupe plus grand : Abstract Factory crée une famille d'objets formant un objet de groupe plus grand, pas un objet unique.
  • Plus tard, ajoutez un nouveau style : si nous continuions à utiliser la méthode d'usine et essayions d'ajouter un nouvel ensemble de styles à l'infrastructure existante, ce serait pénible. Avec Abstract Factory, tout ce que nous avons à faire est simplement de créer une nouvelle usine concrète qui implémente la classe de fabrique abstraite.

Les contre-exemples seraient

  • Une pièce de voiture pour voiture de sport utilisée dans une berline. Cette incohérence peut entraîner des accidents.
  • Un bouton de style Windows dans différents widgets de l'interface graphique du système d'exploitation. Cela ne va rien casser mais nuire à l'expérience utilisateur pour certaines personnes, comme moi.
  • Plus tard, nous découvrons que notre logiciel doit s'exécuter dans la prochaine mise à niveau du système d'exploitation, qui nécessite un ensemble différent d'objets système compatibles tout en gardant le logiciel rétrocompatible.

Par conséquent, lorsqu'un groupe d'objets final doit avoir le même style sans exception d'objet et que vous voulez masquer ce détail "en gardant le même style", alors nous devrions utiliser Abstract Factory.


0

Autant que je comprends la signification des définitions de fabrique abstraite et de méthode Factory, la première est implémentée dans un contexte statique et fournit un objet basé sur des paramètres d'entrée.

Le second utilise un objet déjà créé (la famille) qui implémente l'interface de méthode d'usine. La méthode de fabrique crée ensuite une instance spécifique liée à l'objet d'origine, quel qu'il soit.

Donc, cela conduit généralement à utiliser les deux modèles ensemble où, dans la première étape, vous créez un objet général qui décrit une famille d'objets associés. Il est appelé par la méthode statique getInstance ("mon nom de famille"). L'implémentation d'une telle méthode getInstance décide quel objet de famille sera créé.

Ensuite, j'appelle la méthode createProduct () sur l'objet de famille nouvellement créé et selon l'objet de famille, le nouveau produit sera retourné.

Il semble que ces modèles coopèrent à chacun.

En d'autres termes, Abstract Factory se concentre sur "QUOI" sera créé et la méthode Factory "COMMENT" sera créée.


0

Tout ce que vous devez vous rappeler, c'est qu'une usine abstraite est une usine qui peut renvoyer plusieurs usines . Donc, si vous aviez une AnimalSpeciesFactory, elle peut renvoyer des usines comme celle-ci:

Mamalfactory, BirdFactory, Fishfactory, ReptileFactory. Maintenant que vous avez une seule fabrique de AnimalSpeciesFactory, ils utilisent le modèle de fabrique pour créer des objets spécifiques. Par exemple, imaginez que vous ayez une ReptileFactory de cette AnimalFactory, alors vous pourriez proposer de créer des objets de reptile comme: des objets de serpents, de tortues, de lézards.


0
/*
//Factory methods:

//1. Factory Method - Abstract Creator Class



#include <iostream>
#include <string.h>
using namespace std;

const std::string nineNintyCC = std::string("990CC");
const std::string thousandTwoHundredCC = std::string("1200CC");
const std::string ThousandFiveHundredCC = std::string("1500CC");
const std::string fiveThousandCC = std::string("5000CC");

// Product
class Engine
{
    public:
    virtual void packEngine() = 0;  
};

// Concrete products
// concrete product class one
class C990CCEngine: public Engine
{

    public:
    void packEngine()
    {
       cout << "Pack 990CC engine" << endl;   
    }
};

// concrete class Two
class C1200CCEngine: public Engine
{   public:
    void packEngine()
    {
        cout << "pack 1200CC engine" << endl;
    }

};

// Concrete class Three
class C1500CCEngine: public Engine
{
    public:
    void packEngine()
    {
        cout << "Pack 1500CC engine" << endl;
    }

};


// Car Factory:
class CarFactory{
    public:

    virtual Engine* createEngine(const std::string& type) = 0;
};
class Factory: public CarFactory
{
    public:
     Engine *createEngine(const std::string& type)
     {

          if(0 == nineNintyCC.compare(type))
          {    
             return new C990CCEngine;
          }
          else if(0 == thousandTwoHundredCC.compare(type))
          {
             return new C1200CCEngine;
          }
          else if(0 == ThousandFiveHundredCC.compare(type))
          {
             return new C1500CCEngine;
          } 
          else
           {
                 cout << "Invalid factory input" << endl;
             return NULL;
           }
           return NULL;
     }
};

int main()
{

    CarFactory* ptr = new Factory;
    Engine*pEngine =  ptr->createEngine(nineNintyCC);
    if(pEngine)
    {
        pEngine->packEngine();
        delete pEngine;
    }
    else
    {
        cout << "No engine exists of your type in our factory" << endl;
    }
    pEngine =  ptr->createEngine(ThousandFiveHundredCC);
    if(pEngine)
    {
        pEngine->packEngine();
        delete pEngine;
    }
    else
    {
        cout << "No engine exists of your type in our factory" << endl;
    }
    pEngine =  ptr->createEngine(thousandTwoHundredCC);
    if(pEngine)
    {
        pEngine->packEngine();
        delete pEngine;
    }
    else
    {
        cout << "No engine exists of your type in our factory" << endl;
    }
    pEngine = ptr-> createEngine(fiveThousandCC);
    if(pEngine)
    {
        pEngine->packEngine();
        delete pEngine;
    }
    else
    {
        cout << "No engine exists of your type in our factory" << endl;
    }
    return 0;
}

*/
/*
//
// interface product
#include <iostream>
#include <string>
using namespace std;

class Engine
{
 public:
 virtual void EngineType() = 0;

};

// concrte product
class AltoEngine: public Engine
{
  public:
  void EngineType()
  {
      cout << "Alto Engine" << endl;
  }
};

//Concrte product
class SwiftEngine : public Engine
{
    public:
    void EngineType()
    {
        cout << "Swift Engine" << endl;    
    }
};

class Body
{
   public:
    virtual void bodyType() = 0;

};

class AltoBody: public Body
{
  public:  
    virtual void bodyType()
    {
        cout << "Alto Car Body" << endl;
    }
};

class SwiftBody : public Body
{
    public:
    void bodyType()
    {
        cout << "SwiftCar Body" << endl;
    }

};


class CarFactory
{
   public:
   virtual Engine* createEngineProduct() = 0;
   virtual Body*   createBodyPoduct() = 0;
};
class AltoCarFactory: public CarFactory
{
    public:
    Engine * createEngineProduct()
    {
        return new AltoEngine;
    }
    Body* createBodyPoduct()
    {
        return new AltoBody;
    }

};

class SwiftCarFactory: public CarFactory
{
    public:
    Engine * createEngineProduct()
    {
        return new SwiftEngine;
    }
    Body* createBodyPoduct()
    {
        return new SwiftBody;
    }

};

int main()
{

    CarFactory* pAltoFactory = new AltoCarFactory;
    Engine* pAltoEngine = pAltoFactory->createEngineProduct();
    pAltoEngine->EngineType();
    Body* pAltoBody = pAltoFactory->createBodyPoduct();
    pAltoBody->bodyType();



    CarFactory* pSwiftFactory = NULL;
    pSwiftFactory = new SwiftCarFactory;
    Engine* pSwiftEngine = pSwiftFactory->createEngineProduct();
    pSwiftEngine->EngineType();
    Body* pSwfitBody = pSwiftFactory->createBodyPoduct();
    pSwfitBody->bodyType();
    delete pAltoBody;
    delete pAltoFactory;
    delete pSwfitBody;
    delete pSwiftFactory;
    return 0;
}
*/

/*

// One more Factory example;

#include <iostream>
#include <string>
using namespace std;

const std::string maruthi = std::string("Maruthi");
const std::string fiat = std::string("Fiat");
const std::string renault = std::string("Renault");
// Interface
class CarEngine
{
 public:
    virtual void engineType() = 0;
};

// Concrete class
class FiatEngine: public CarEngine
{
  public:
  void engineType()
  {
      cout << "Fait Engine Engine" << endl;
  }

};
// ConcreteClass
class RenaultEngine : public CarEngine
{
    public:
    void engineType()
    {
        cout << "Renault Engine" << endl;
    }

};
// Concrete class
class MaruthiEngine : public CarEngine
{
    public:
    void engineType()
    {
        cout << "Maruthi Engine" << endl;
    }
};


// Factory
class CarFactory
{
    public:
    virtual CarEngine* createFactory(const std::string&) = 0;
};

// EngineFactory
class CarEngineFactory : public CarFactory
{
     public:
     CarEngine* createFactory(const std::string&  type)
     {
          if(0 == maruthi.compare(type))
          {
              return new MaruthiEngine;

          }
          else if(0 == fiat.compare(type))
          {
              return  new FiatEngine;
          }
          else if(0 == renault.compare(type))
          {
              return new RenaultEngine;
          }
          else
          {
              cout << "Invalid Engine type" << endl;
              return NULL;
          }
     }

  };

int main()
{
    CarFactory* pCarFactory = new CarEngineFactory;
    CarEngine* pMaruthiCarEngine = pCarFactory->createFactory(maruthi);
    pMaruthiCarEngine->engineType();

    CarEngine* pFiatCarEngine = pCarFactory->createFactory(fiat);
    pFiatCarEngine->engineType();


    CarEngine* pRenaultCarEngine = pCarFactory->createFactory(renault);
    pRenaultCarEngine->engineType();

    return 0;
}


*/


/*

// One more Factory example;

#include <iostream>
#include <string>
using namespace std;

const std::string maruthi = std::string("Maruthi");
const std::string fiat = std::string("Fiat");
const std::string renault = std::string("Renault");


// Interface
class CarEngine
{
 public:
    virtual void engineType() = 0;
};

// Concrete class
class FiatEngine: public CarEngine
{
  public:
  void engineType()
  {
      cout << "Fait Car Engine" << endl;
  }

};

// ConcreteClass
class RenaultEngine : public CarEngine
{
    public:
    void engineType()
    {
        cout << "Renault Car Engine" << endl;
    }

};

// Concrete class
class MaruthiEngine : public CarEngine
{
    public:
    void engineType()
    {
        cout << "Maruthi Car Engine" << endl;
    }
};

// Interface
class CarBody
{
 public:
    virtual void bodyType() = 0;
};

// Concrete class
class FiatBody: public CarBody
{
  public:
  void bodyType()
  {
      cout << "Fait car Body" << endl;
  }

};

// ConcreteClass
class RenaultBody : public CarBody
{
    public:
    void bodyType()
    {
        cout << "Renault Body" << endl;
    }

};

// Concrete class
class MaruthiBody : public CarBody
{
    public:
    void bodyType()
    {
        cout << "Maruthi body" << endl;
    }
};


// Factory
class CarFactory
{
    public:
    virtual CarEngine* createCarEngineProduct() = 0;
    virtual CarBody* createCarBodyProduct() = 0;
};

// FiatFactory
class FaitCarFactory : public CarFactory
{
     public:
     CarEngine* createCarEngineProduct()
     {
        return new FiatEngine; 
     }
     CarBody* createCarBodyProduct()
     {
         return new FiatBody;
     }
};

// Maruthi Factory
class MaruthiCarFactory : public CarFactory
{
     public:
     CarEngine* createCarEngineProduct()
     {
         return new MaruthiEngine;
     }
     CarBody* createCarBodyProduct()
     {
         return new MaruthiBody;
     }

};

// Renault Factory
class RenaultCarFactory : public CarFactory
{
     public:
    CarEngine* createCarEngineProduct()
    {
        return new RenaultEngine;
    }

    CarBody* createCarBodyProduct()
    {
        return new RenaultBody;
    }

};


int main()
{

   // Fiat Factory
   CarFactory* pFiatCarFactory = new FaitCarFactory;
   CarEngine* pFiatEngine = pFiatCarFactory->createCarEngineProduct();
   CarBody*  pFiatBody = pFiatCarFactory->createCarBodyProduct();
   pFiatEngine->engineType();
   pFiatBody->bodyType();

   // Renault Car Factory
    return 0;
}

*/

-1

Le modèle de méthode d'usine est un modèle de conception créative qui traite de la création d'objets sans montrer la classe exacte d'objet en cours de création. Ce modèle de conception permet essentiellement à une classe de différer l'instanciation à des sous-classes.

Le modèle Abstract Factory sert à l'encapsulation à un groupe d'usines individuelles sans exposer les classes concrètes. Dans ce modèle, une interface générique d'une classe d'usine abstraite est utilisée pour créer l'objet concret requis séparant les détails d'implémentation des objets de leur utilisation et de leur composition. Ce modèle de conception est largement utilisé dans les applications GUI où un type similaire de composants GUI doit être créé.

en cherchant sur google, je suis venu après le blog qui a expliqué les deux modèles de conception avec brio. jetez un œil sur ces

http://simpletechtalks.com/factory-design-pattern/

http://simpletechtalks.com/abstract-factory-design-pattern/

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.