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.

0voto

Vincent Robert Points 16530

Si vous prévoyez de prendre en charge des plugins liés dynamiquement, votre programme devra fournir une ABI (interface binaire d'application) stable, ce qui signifie que vous ne pouvez pas utiliser C++ comme interface principale, car C++ n'a pas d'ABI standard.

Si vous voulez que les plugins mettent en œuvre une interface que vous définissez vous-même, vous devrez fournir le fichier d'en-tête de l'interface au programmeur du plugin et standardiser une interface C très simple pour créer et supprimer l'objet.

Vous ne pouvez pas fournir une bibliothèque dynamique qui vous permette de "créer" la classe du plugin telle quelle. C'est pourquoi vous devez standardiser sur une interface C afin de créer l'objet. L'utilisation de l'objet C++ est alors possible tant qu'aucun de vos arguments n'utilise des types éventuellement incompatibles, comme les conteneurs STL. Vous ne pourrez pas utiliser un vecteur renvoyé par une autre bibliothèque, car vous ne pouvez pas vous assurer que leur implémentation STL est la même que la vôtre.

Manager.h

class Manager
{
public:
  virtual void doSomething() = 0;
  virtual int doSomethingElse() = 0;
}

extern "C" {
Manager* newManager();
void deleteManager(Manager*);
}

PluginManager.h

#include "Manager.h"

class PluginManager : public Manager
{
public:
  PluginManager();
  virtual ~PluginManager();

public:
  virtual void doSomething();
  virtual int doSomethingElse();
}

PluginManager.cpp

#include "PluginManager.h"

Manager* newManager()
{
  return new PluginManager();
}
void deleteManager(Manager* pManager)
{
  delete pManager;
}

PluginManager::PluginManager()
{
  // ...
}

PluginManager::~PluginManager()
{
  // ...
}

void PluginManager::doSomething()
{
  // ...
}

int PluginManager::doSomethingElse()
{
  // ...
}

0voto

acidzombie24 Points 28569

Tu n'as pas parlé de TheManager. On dirait que vous voulez qu'il contrôle quelle classe est utilisée ? ou peut-être essayez-vous de les enchaîner ?

Il semble que vous ayez besoin d'une classe de base abstraite et d'un pointeur vers la classe actuellement utilisée. Si vous souhaitez chaîner, vous pouvez le faire à la fois dans la classe abstraite et dans la classe du gestionnaire. Si c'est une classe abstraite, ajoutez un membre à la classe suivante dans la chaîne, si c'est un gestionnaire, triez-le dans l'ordre dans lequel vous voulez l'utiliser dans une liste. Vous aurez besoin d'un moyen d'ajouter des classes, donc vous aurez besoin d'un addMe() dans themanager. Il semble que vous savez ce que vous faites, donc ce que vous choisissez devrait être correct. Une liste avec une fonction addMe est ma recommandation et si vous ne voulez qu'une seule classe active alors une fonction dans TheManager la décidant serait bonne.

0voto

jyoung Points 2598

C'est peut-être plus lourd que ce dont vous avez besoin, mais il semble que vous essayez de créer une classe de travail qui supporte les plugins.

Je le diviserais en trois sections.

1) La classe FrameWork serait propriétaire des plugins. Cette classe est responsable de la publication des interfaces fournies par les plugins.

2) Une classe PlugIn posséderait les composants qui font le travail. Cette classe est responsable de l'enregistrement des interfaces exportées et de la liaison des interfaces importées aux composants.

3) La troisième section, les composants sont les fournisseurs et les consommateurs des interfaces.

Pour rendre les choses extensibles, la mise en place et le fonctionnement peuvent être divisés en plusieurs étapes.

  1. Créez tout.
  2. Connectez tout.
  3. Commencez tout.

Pour décomposer les choses.

  1. Arrêtez tout.
  2. Détruisez tout.

    class IFrameWork { public: virtual ~IFrameWork() {} virtual void RegisterInterface( const char*, void* ) = 0; virtual void* GetInterface( const char* name ) = 0; };

    class IPlugIn { public: virtual ~IPlugIn() {} virtual void BindInterfaces( IFrameWork* frameWork ) {}; virtual void Start() {}; virtual void Stop() {}; };

    struct SamplePlugin :public IPlugIn { ILogger* logger;

    Component1 component1;
    WebServer  webServer;
    public:
    SamplePlugin( IFrameWork* frameWork )

    logger( (ILogger*)frameWork->GetInterface( "ILogger" ) ), //assumes the 'System' plugin exposes this component1(), webServer( component1 ) { logger->Log( "MyPlugin Ctor()" );

    frameWork->RegisterInterface( "ICustomerManager", dynamic\_cast( &component1 ) ); 
    frameWork->RegisterInterface( "IVendorManager", dynamic\_cast( &component1 ) ); 
    frameWork->RegisterInterface( "IAccountingManager", dynamic\_cast( &webServer ) ); 

    }

    virtual void BindInterfaces( IFrameWork* frameWork ) { logger->Log( "MyPlugin BindInterfaces()" );

    IProductManager\* productManager( static\_cast( frameWork->GetInterface( "IProductManager" ) ) );
    IShippingManager\* shippingManager( static\_cast( frameWork->GetInterface( "IShippingManager" ) ) );
    
    component1.BindInterfaces( logger, productManager );
    webServer.BindInterfaces( logger, productManager, shippingManager );

    }

    virtual void Start() { logger->Log( "MyPlugin Start()" );

    webServer.Start();

    }

    virtual void Stop() { logger->Log( "MyPlugin Stop()" );

    webServer.Stop();

    } };

    class FrameWork :public IFrameWork { vector plugIns; map interfaces; public: virtual void RegisterInterface( const char* name, void* itfc ) { interfaces[ name ] = itfc; } virtual void* GetInterface( const char* name ) { return interfaces[ name ]; }

    FrameWork() {
        //Only interfaces in 'SystemPlugin' can be used by all methods of the other plugins
        plugIns.push\_back( new SystemPlugin( this ) );
    
        plugIns.push\_back( new SamplePlugin( this ) ); 
        //add other plugIns here
    
        for\_each( plugIns.begin(), plugIns.end(), bind2nd( mem\_fun( &IPlugIn::BindInterfaces ), this ) );
        for\_each( plugIns.begin(), plugIns.end(), mem\_fun( &IPlugIn::Start ) );
    }
    
    ~FrameWork() {
        for\_each( plugIns.rbegin(), plugIns.rend(), mem\_fun( &IPlugIn::Stop ) );
        for\_each( plugIns.rbegin(), plugIns.rend(), Delete() );
    }

    };

0voto

Dave Van den Eynde Points 8199

Voici une implémentation minimale du factory pattern que j'ai réalisée en 15 minutes environ. Nous utilisons un modèle similaire qui utilise des classes de base plus avancées.

#include "stdafx.h"
#include <map>
#include <string>

class BaseClass
{
public:
    virtual ~BaseClass() { }
    virtual void Test() = 0;
};

class DerivedClass1 : public BaseClass 
{ 
public:
    virtual void Test() { } // You can put a breakpoint here to test.
};

class DerivedClass2 : public BaseClass 
{ 
public:
    virtual void Test() { } // You can put a breakpoint here to test.
};

class IFactory
{
public:
    virtual BaseClass* CreateNew() const = 0;
};

template <typename T>
class Factory : public IFactory
{
public:
    T* CreateNew() const { return new T(); }
};

class FactorySystem
{
private:
    typedef std::map<std::wstring, IFactory*> FactoryMap;
    FactoryMap m_factories;

public:
    ~FactorySystem()
    {
        FactoryMap::const_iterator map_item = m_factories.begin();
        for (; map_item != m_factories.end(); ++map_item) delete map_item->second;
        m_factories.clear();
    }

    template <typename T>
    void AddFactory(const std::wstring& name)
    {
        delete m_factories[name]; // Delete previous one, if it exists.
        m_factories[name] = new Factory<T>();
    }

    BaseClass* CreateNew(const std::wstring& name) const
    {
        FactoryMap::const_iterator found = m_factories.find(name);
        if (found != m_factories.end())
            return found->second->CreateNew();
        else
            return NULL; // or throw an exception, depending on how you want to handle it.
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    FactorySystem system;
    system.AddFactory<DerivedClass1>(L"derived1");
    system.AddFactory<DerivedClass2>(L"derived2");

    BaseClass* b1 = system.CreateNew(L"derived1");
    b1->Test();
    delete b1;
    BaseClass* b2 = system.CreateNew(L"derived2");
    b2->Test();
    delete b2;

    return 0;
}

Il suffit de faire un copier-coller d'une première application Win32 en console dans VS2005/2008. J'aime souligner quelque chose :

  • Il n'est pas nécessaire de créer une fabrique concrète pour chaque classe. Un modèle le fera pour vous.
  • J'aime placer l'ensemble du modèle de fabrique dans sa propre classe, de sorte que vous n'ayez pas à vous soucier de la création et de la suppression des objets de fabrique. Vous enregistrez simplement vos classes, une classe de fabrique est créée par le compilateur et un objet de fabrique est créé par le modèle. À la fin de sa vie, toutes les fabriques sont proprement détruites. J'aime cette forme d'encapsulation, car il n'y a pas de confusion quant à savoir qui régit la durée de vie des fabriques.

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