13 votes

Comment concevoir une simple fabrique d'objets C++ ?

Dans mon application, il y a 10-20 classes qui sont instanciées une fois [*]. Voici un exemple :

class SomeOtherManager;

class SomeManagerClass {
public:
    SomeManagerClass(SomeOtherManager*);
    virtual void someMethod1();
    virtual void someMethod2();
};

Les instances des classes sont contenues dans un seul objet :

class TheManager {
public:
    virtual SomeManagerClass* someManagerClass() const;
    virtual SomeOtherManager* someOtherManager() const;
    /** More objects... up to 10-20 */
};

Actuellement, TheManager utilise le nouveau afin de créer des objets.

Mon intention est de pouvoir remplacer, à l'aide de plugins, l'implémentation de SomeManagerClass (ou de toute autre classe) par une autre. Afin de remplacer l'implémentation, 2 étapes sont nécessaires :

  1. Définir une classe DerivedSomeManagerClass, qui hérite de SomeManagerClass [plugin].
  2. Créez la nouvelle classe (DerivedSomeManagerClass) au lieu de la classe par défaut (SomeManagerClass) [application].

Je suppose que j'ai besoin d'une sorte de fabrique d'objets, mais cela devrait être assez simple puisqu'il n'y a toujours qu'un seul type à créer (l'implémentation par défaut ou l'implémentation utilisateur).

Une idée sur la façon de concevoir une usine simple comme celle que je viens de décrire ? Tenez compte du fait qu'il pourrait y avoir d'autres classes à l'avenir, et qu'il devrait donc être facile à étendre.

[*] Je m'en fiche si ça arrive plus d'une fois.

Edit : Veuillez noter que plus de deux objets sont contenus dans TheManager.

2voto

vishvananda Points 136

Cela semble beaucoup plus simple avec un modèle de fonction qu'avec un modèle de fabrique abstraite.

class ManagerFactory
{
public:
    template <typename T> static BaseManager * getManager() { return new T();}
};

BaseManager * manager1 = ManagerFactory::template getManager<DerivedManager1>();

Si vous voulez les obtenir via une chaîne de caractères, vous pouvez créer une carte standard de chaînes de caractères vers des pointeurs de fonction. Voici une implémentation qui fonctionne :

#include <map>
#include <string>

class BaseManager
{
public:
    virtual void doSomething() = 0;
};

class DerivedManager1 : public BaseManager
{
public:
    virtual void doSomething() {};
};

class DerivedManager2 : public BaseManager
{
public:
    virtual void doSomething() {};
};

class ManagerFactory
{
public:
    typedef BaseManager * (*GetFunction)();
    typedef std::map<std::wstring, GetFunction> ManagerFunctionMap;
private:
    static ManagerFunctionMap _managers;

public:
    template <typename T> static BaseManager * getManager() { return new T();}
    template <typename T> static void registerManager(const std::wstring& name)
    {
        _managers[name] = ManagerFactory::template getManager<T>;
    }
    static BaseManager * getManagerByName(const std::wstring& name)
    {
        if(_managers.count(name))
        {
            return _managers[name]();
        }
        return NULL;
    }
};
// the static map needs to be initialized outside the class
ManagerFactory::ManagerFunctionMap ManagerFactory::_managers;

int _tmain(int argc, _TCHAR* argv[])
{
    // you can get with the templated function
    BaseManager * manager1 = ManagerFactory::template getManager<DerivedManager1>();
    manager1->doSomething();
    // or by registering with a string
    ManagerFactory::template registerManager<DerivedManager1>(L"Derived1");
    ManagerFactory::template registerManager<DerivedManager2>(L"Derived2");
    // and getting them
    BaseManager * manager2 = ManagerFactory::getManagerByName(L"Derived2");
    manager2->doSomething();
    BaseManager * manager3 = ManagerFactory::getManagerByName(L"Derived1");
    manager3->doSomething();
    return 0;
}

EDIT : En lisant les autres réponses, je me suis rendu compte que cette solution est très similaire à la solution FactorySystem de Dave Van den Eynde, mais j'utilise un pointeur de modèle de fonction au lieu d'instancier des classes d'usine modélisées. Je pense que ma solution est un peu plus légère. Grâce aux fonctions statiques, le seul objet qui est instancié est la carte elle-même. Si vous avez besoin de la fabrique pour exécuter d'autres fonctions (DestroyManager, etc.), je pense que sa solution est plus extensible.

1voto

boutta Points 4873

Vous pouvez implémenter une fabrique d'objets avec des méthodes statiques qui renvoient une instance d'un Manager-Class. Dans la fabrique, vous pourriez créer une méthode pour le type de gestionnaire par défaut et une méthode pour n'importe quel type de gestionnaire auquel vous donnez un argument représentant le type de Manager-Class (disons avec une énumération). Cette dernière méthode devrait retourner une interface plutôt qu'une classe.

Edit : Je vais essayer de donner un peu de code, mais gardez à l'esprit que mon expérience en C++ est assez ancienne et que je ne fais que du Java et quelques scripts pour le moment.

class Manager { // aka Interface
    public: virtual void someMethod() = 0;
};

class Manager1 : public Manager {
    void someMethod() { return null; }
};

class Manager2 : public Manager {
    void someMethod() { return null; }
};

enum ManagerTypes {
    Manager1, Manager2
};

class ManagerFactory {
    public static Manager* createManager(ManagerTypes type) {
        Manager* result = null;
        switch (type) {
        case Manager1:
             result = new Manager1();
             break;
        case Manager2:
             result = new Manager2();
             break;
        default:
             // Do whatever error logging you want
             break;
        }
        return result;
     }
 };

Vous devriez maintenant être en mesure d'appeler l'usine via (si vous avez réussi à faire fonctionner l'exemple de code) :

Manager* manager = ManagerFactory.createManager(ManagerTypes.Manager1);

1voto

David Allan Finch Points 909

J'utiliserais des modèles comme celui-ci, car je ne vois pas l'intérêt des classes d'usines :

class SomeOtherManager;

class SomeManagerClass {
public:
    SomeManagerClass(SomeOtherManager*);
    virtual void someMethod1();
    virtual void someMethod2();
};

class TheBaseManager {
public:
      // 
};

template <class ManagerClassOne, class ManagerClassOther> 
class SpecialManager : public TheBaseManager {
    public:
        virtual ManagerClassOne* someManagerClass() const;
        virtual ManagerClassOther* someOtherManager() const;
};

TheBaseManager* ourManager = new SpecialManager<SomeManagerClass,SomeOtherManager>;

1voto

Vous devriez jeter un coup d'œil au tutoriel à l'adresse suivante http://downloads.sourceforge.net/papafactory/PapaFactory20080622.pdf?use_mirror=fastbull

Il contient un excellent tutoriel sur l'implémentation d'une fabrique abstraite en C++ et le code source qui l'accompagne est également très robuste.

Chris

0voto

Ronny Brendel Points 2588

Mh je ne comprends pas à cent pour cent, et je ne suis pas vraiment dans l'usine de trucs à partir de livres et d'articles.


Si tous vos gestionnaires partagent une interface similaire, vous pouvez dériver d'une classe de base, et utiliser cette classe de base dans votre programme. Selon l'endroit où la décision de créer une classe sera prise, vous devez utiliser un identifiant pour la création (comme indiqué ci-dessus) ou gérer la décision du gestionnaire à instancier en interne.


Une autre solution consisterait à mettre en œuvre une "politique" en utilisant des modèles. De sorte que vous ManagerClass::create() renvoie une instance spécifique de SomeOtherManagerWhatever. Cela mettrait la décision sur le gestionnaire à prendre dans le code qui utilise votre gestionnaire - Peut-être que ce n'est pas voulu.

Ou par là :


template<class MemoryManagment>
class MyAwesomeClass
{
    MemoryManagment m_memoryManager;
};

(ou quelque chose comme ça) Avec cette construction, vous pouvez facilement utiliser d'autres gestionnaires en modifiant seulement l'instanciation de MyAwesomeClass.


Aussi, une classe dans ce but pourrait être un peu exagérée. Dans votre cas, une fonction d'usine ferait l'affaire, je suppose. C'est plus une question de préférence personnelle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X