문제

내 응용 프로그램에는 한 번 인스턴스화되는 10-20 개의 클래스가 있습니다 [*]. 예는 다음과 같습니다.

class SomeOtherManager;

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

클래스의 인스턴스는 하나의 객체에 포함되어 있습니다.

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

현재 Themanager를 사용합니다 새로운 객체를 만들기 위해 연산자.

내 의도는 플러그인을 사용하여 SomemanagerClass (또는 다른 클래스) 구현을 다른 것과 교체 할 수 있다는 것입니다. 구현을 교체하려면 2 단계가 필요합니다.

  1. somemanagerClass [플러그인]을 상속받는 클래스 파생물을 정의합니다.
  2. 기본값 (somemanagerClass) 대신 새 클래스 (devivedsomemanagerClass)를 만듭니다 [응용 프로그램

나는 일종의 객체 공장이 필요하다고 생각하지만, 항상 하나의 유형 (기본 구현 또는 사용자 구현)이 있기 때문에 상당히 간단해야합니다.

방금 설명한 것처럼 간단한 공장을 설계하는 방법에 대한 아이디어가 있습니까? 앞으로 더 많은 수업이있을 수 있다는 사실을 고려하여 확장하기 쉽습니다.

*] 두 번 이상 발생하는지 상관하지 않습니다.

편집하다: Themanager에는 2 개 이상의 객체가 포함되어 있습니다.

도움이 되었습니까?

해결책

여기에는 두 가지 별도의 문제가 있다고 생각합니다.

한 가지 문제는 다음과 같습니다. Themanager는 어떻게합니까? 이름 그것이 만들어야하는 수업? "클래스를 만드는 방법"에 대한 일종의 포인터를 유지해야합니다. 가능한 솔루션은 다음과 같습니다.

  • 각 종류의 클래스에 대해 별도의 포인터를 유지하고 설정하는 방법으로, 당신은 이미 건식 원칙을 위반하기 때문에 이것을 좋아하지 않는다고 이미 말했습니다.
  • 열쇠가 열거 나 문자열 인 곳에 어떤 종류의 테이블을 유지합니다. 이 경우 세터는 매개 변수가있는 단일 함수입니다 (물론 키가 열거 인 경우 맵 대신 벡터를 사용할 수 있습니다).

다른 문제는 다음과 같습니다. 이것이 "수업을 만드는 방법"은 무엇입니까? 불행히도 우리는 포인터를 생성자에게 직접 저장할 수는 없지만 다음을 수행 할 수 있습니다.

  • 다른 사람들이 지적했듯이 각 수업마다 공장을 만들어냅니다.
  • 각 클래스에 대한 정적 "Create"함수를 추가하십시오. 일관된 서명을 유지하면 포인터를 사용하여 기능을 수행 할 수 있습니다.

템플릿은 두 경우 모두 불필요한 코드 복제를 피하는 데 도움이 될 수 있습니다.

다른 팁

SomemanagerClass에서 상속되는 클래스 (Plugin1)를 가정하면 유형을 구축하려면 클래스 계층이 필요합니다.

class factory
{
public:
    virtual SomeManagerClass* create() = 0;
};

class plugin1_factory : public factory
{
public:
    SomeManagerClass* create() { return new plugin1(); }
};

그런 다음 해당 공장을 STD ::지도에 할당 할 수 있습니다.

std::map<string, factory*>  factory_map;
...
factory_map["plugin1"] = new plugin1_factory();

마지막으로 Themanager는 플러그인의 이름 (문자열로)을 알아야하며 한 줄의 코드만으로 SomemanagerClass 유형의 객체를 반환 할 수 있습니다.

SomeManagerClass* obj = factory_map[plugin_name]->create();

편집하다: 각 플러그인에 대해 하나의 플러그인 공장 클래스가 마음에 들지 않으면 이전 패턴을 수정할 수 있습니다.

template <class plugin_type>
class plugin_factory : public factory
{
public:
   SomeManagerClass* create() { return new plugin_type(); }
};

factory_map["plugin1"] = new plugin_factory<plugin1>();

나는 이것이 훨씬 더 나은 솔루션이라고 생각합니다. 또한 'Plugin_Factory'클래스는 문자열을 Costructor를 통과하면 'factory_map'에 추가 할 수 있습니다.

나는 C ++ 공장에 대한 또 다른 질문에 대답했다. 참조하십시오 거기 유연한 공장이 관심이있는 경우. 나는 ET ++의 오래된 방법을 설명하려고 노력하여 나에게 큰 도움이 된 매크로를 사용합니다.

ET ++ Old MacApp을 C ++ 및 X11로 포트하는 프로젝트였습니다. 그것의 노력으로 Eric Gamma 등은 생각하기 시작했습니다. 디자인 패턴

나는 모든 기본 관리자의 생성을위한 가상 방법을 갖는 "기본"공장을 만들고, "Meta Manager"(질문의 Themanager)가 기본 공장에 대한 포인터를 생성자 매개 변수로 가져 가게합니다.

"공장"이 CXYZWManager의 인스턴스를 파생하여 CXYZWManager의 인스턴스를 사용자 정의 할 수 있다고 가정하지만, 또는 CXYZWMANAGER의 생성자는 "사용자 정의"공장에서 다른 주장을 할 수 있습니다.

"csomemanager"및 "cderivedfromsomemanager"를 출력하는 긴 코드 예제 :

#include <iostream>
//--------------------------------------------------------------------------------
class CSomeManager
  {
  public:
    virtual const char * ShoutOut() { return "CSomeManager";}
  };

//--------------------------------------------------------------------------------
class COtherManager
  {
  };

//--------------------------------------------------------------------------------
class TheManagerFactory
  {
  public:
    // Non-static, non-const to allow polymorphism-abuse
    virtual CSomeManager   *CreateSomeManager() { return new CSomeManager(); }
    virtual COtherManager  *CreateOtherManager() { return new COtherManager(); }
  };

//--------------------------------------------------------------------------------
class CDerivedFromSomeManager : public CSomeManager
  {
  public:
    virtual const char * ShoutOut() { return "CDerivedFromSomeManager";}
  };

//--------------------------------------------------------------------------------
class TheCustomManagerFactory : public TheManagerFactory
  {
  public:
    virtual CDerivedFromSomeManager        *CreateSomeManager() { return new CDerivedFromSomeManager(); }

  };

//--------------------------------------------------------------------------------
class CMetaManager
  {
  public:
    CMetaManager(TheManagerFactory *ip_factory)
      : mp_some_manager(ip_factory->CreateSomeManager()),
        mp_other_manager(ip_factory->CreateOtherManager())
      {}

    CSomeManager  *GetSomeManager()  { return mp_some_manager; }
    COtherManager *GetOtherManager() { return mp_other_manager; }

  private:
    CSomeManager  *mp_some_manager;
    COtherManager *mp_other_manager;
  };

//--------------------------------------------------------------------------------
int _tmain(int argc, _TCHAR* argv[])
  {
  TheManagerFactory standard_factory;
  TheCustomManagerFactory custom_factory;

  CMetaManager meta_manager_1(&standard_factory);
  CMetaManager meta_manager_2(&custom_factory);

  std::cout << meta_manager_1.GetSomeManager()->ShoutOut() << "\n";
  std::cout << meta_manager_2.GetSomeManager()->ShoutOut() << "\n";
  return 0;
  }

여기에 내가 생각한 솔루션이 있습니다. 최고의 솔루션은 아니지만 더 나은 솔루션을 생각하는 데 도움이 될 것입니다.

각 수업마다 제작자 수업이 있습니다.

class SomeManagerClassCreator {
public:
    virtual SomeManagerClass* create(SomeOtherManager* someOtherManager) { 
        return new SomeManagerClass(someOtherManager); 
    }
};

그런 다음 제작자는 한 수업에서 수집됩니다.

class SomeManagerClassCreator;
class SomeOtherManagerCreator;

class TheCreator {
public:
    void setSomeManagerClassCreator(SomeManagerClassCreator*);
    SomeManagerClassCreator* someManagerClassCreator() const;

    void setSomeOtherManagerCreator(SomeOtherManagerCreator*);
    SomeOtherManagerCreator* someOtherManagerCreator() const;
private:
    SomeManagerClassCreator* m_someManagerClassCreator;
    SomeOtherManagerCreator* m_someOtherManagerCreator;
};

그리고 Themanager는 내부 생성을 위해 thecreator와 함께 만들어 질 것입니다.

class TheManager {
public:
    TheManager(TheCreator*);
    /* Rest of code from above */
};

이 솔루션의 문제점은 건조를 위반한다는 것입니다. 각 클래스 제작자마다 세터/getter를 thecreator에 작성해야합니다.

추상 공장 패턴이 아닌 기능 템플릿이 훨씬 더 단순한 것 같습니다.

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

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

문자열을 통해 가져 오려면 문자열에서 표준 맵을 만들어 기능 포인터를 만들 수 있습니다. 다음은 작동하는 구현입니다.

#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;
}

편집하다: 다른 답변을 읽을 때 나는 이것이 Dave van Den Eynde의 공장 시스템 솔루션과 매우 유사하다는 것을 깨달았지만 템플릿 공장 클래스를 인스턴스화하는 대신 기능 템플릿 포인터를 사용하고 있습니다. 내 솔루션이 조금 더 가볍다고 생각합니다. 정적 함수로 인해 인스턴스화되는 유일한 객체는 맵 자체입니다. 다른 기능 (DestroyManager 등)을 수행하기 위해 공장이 필요하다면 그의 솔루션이 더 확장 가능하다고 생각합니다.

관리자 클래스 인스턴스를 반환하는 정적 메소드가있는 객체 공장을 구현할 수 있습니다. 공장에서는 기본 유형의 관리자에 대한 메소드와 관리자 클래스 유형 (열거)의 유형을 나타내는 인수를 제공하는 모든 유형의 관리자에 대한 메소드를 만들 수 있습니다. 이 마지막 방법은 클래스가 아닌 인터페이스를 반환해야합니다.

편집 : 코드를 제공하려고 노력하지만 C ++ 시간은 꽤 오래되었고 나는 Java와 당분간 스크립팅 만하고 있다고 생각합니다.

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;
     }
 };

이제 (코드 샘플 작업을 할 수있는 경우) : 공장에 전화 할 수 있어야합니다.

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

공장 수업의 지점을 볼 수 없으므로 이와 같은 템플릿을 사용합니다.

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>;

튜토리얼을 살펴 봐야합니다http://downloads.sourceforge.net/papafactory/papafactory20080622.pdf?use_mirror=fastbull

C ++에서 추상 공장 구현에 대한 훌륭한 자습서가 포함되어 있으며 함께 제공되는 소스 코드도 매우 강력합니다.

크리스

MH 나는 백 %를 이해하지 못하고 책과 기사의 공장에 실제로 들어 가지 않습니다.


모든 관리자가 유사한 인터페이스를 공유하면 기본 클래스에서 파생 될 수 있으며 프로그램 에서이 기본 클래스를 사용할 수 있습니다. 어떤 클래스가 만들어 질지 결정하는 위치에 따라, 위에서 언급 한대로 제작을 위해 식별자를 사용하거나 내부적으로 인스턴스화 할 결정을 처리해야합니다.


또 다른 방법은 템플릿을 사용하는 것과 같은 "정책"을 구현하는 것입니다. ManagerClass :: Create ()가 인스턴스에 관계없이 특정 일부를 반환합니다. 이것은 관리자를 사용하는 코드에서 어떤 관리자가 결정 해야하는지 결정을 내릴 것입니다. Maye는 의도되지 않습니다.

또는 그런 식으로 :


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

이 구성을 사용하면 Myawesomeclass의 인스턴스화 만 변경하여 다른 관리자를 쉽게 사용할 수 있습니다.


또한이 목적을위한 수업은 약간 위에있을 수 있습니다. 귀하의 경우 공장 기능이 추측 할 것입니다. 글쎄, 그것은 개인적인 취향에 대한 문제입니다.

동적으로 연결된 플러그인을 지원하려는 경우 프로그램은 안정적인 ABI (Application Binary Interface)를 제공해야합니다. 즉, C ++에는 표준 ABI가 없으므로 C ++를 기본 인터페이스로 사용할 수 없습니다.

플러그인이 인터페이스를 구현하려면 자신을 정의 할 경우 객체를 작성하고 삭제하려면 인터페이스의 헤더 파일을 플러그인 프로그래머에 제공하고 매우 간단한 C 인터페이스에서 표준화해야합니다.

플러그인 클래스 AS-IS를 "새로"할 수있는 동적 라이브러리를 제공 할 수 없습니다. 그렇기 때문에 객체를 만들려면 C 인터페이스에서 표준화해야합니다. C ++ 객체를 사용하면 STL 컨테이너와 같이 인수가 호환되지 않는 유형을 사용하지 않는 한 가능합니다. STL 구현이 귀하와 동일하다는 것을 확인할 수 없기 때문에 다른 라이브러리에서 반환 된 벡터를 사용할 수 없습니다.

관리자 .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()
{
  // ...
}

당신은 Themanager에 대해 이야기하지 않았습니다. 어떤 클래스가 사용되는지 제어하고 싶은 것 같습니다. 아니면 함께 사슬을하려고할까요?

추상적 인 기본 클래스와 현재 사용되는 클래스에 대한 포인터가 필요한 것 같습니다. 당신이 체인을 원한다면, 당신은 추상 클래스와 Themanager 클래스 모두에서 그것을 할 수 있습니다. 초록 클래스 인 경우, Chain에서 다음 클래스에 멤버를 추가하면 Themanager 인 경우 목록에 사용할 수있는 것을 지정하십시오. 클래스를 추가하는 방법이 필요하므로 Themanager에 addme ()가 필요합니다. 당신이 선택한 것이 당신이 선택한 것이 옳은 일을 알고있는 것 같습니다. AddMe Func가있는 목록은 내 권장 사항이며, 1 개의 활성 클래스 만 원한다면 Themanager의 함수가 좋을 것이라고 결정합니다.

이것은 필요한 것보다 무겁지만 플러그인을 지원하는 프레임 작업 클래스를 만들려고하는 것처럼 들립니다.

나는 그것을 최대 3 개의 섹션으로 나눕니다.

1) 프레임 워크 클래스는 플러그인을 소유합니다. 이 클래스는 플러그인에서 제공하는 인터페이스를 게시 할 책임이 있습니다.

2) 플러그인 클래스는 작업을 수행하는 구성 요소를 소유합니다. 이 클래스는 내보내는 인터페이스를 등록하고 가져온 인터페이스를 구성 요소에 바인딩해야합니다.

3) 세 번째 섹션에서는 구성 요소가 인터페이스의 공급 업체와 소비자입니다.

물건을 확장하기 위해 물건을 올리거나 달리는 것이 무대로 나올 수 있습니다.

  1. 모든 것을 만듭니다.
  2. 모든 것을 연결하십시오.
  3. 모든 것을 시작하십시오.

물건을 분해합니다.

  1. 모든 것을 중지하십시오.
  2. 모든 것을 파괴하다.
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() );
    }
};

다음은 약 15 분 안에 최소한의 공장 패턴 구현입니다. 우리는 고급 기본 클래스를 사용하는 유사한 것을 사용합니다.

#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;
}

VS2005/2008의 초기 Win32 콘솔 앱을 복사하고 붙여 넣으십시오. 나는 무언가를 지적하고 싶다 :

  • 모든 수업에 대한 콘크리트 공장을 만들 필요는 없습니다. 템플릿이 당신을 위해 그렇게 할 것입니다.
  • 나는 공장 전체 패턴을 자체 클래스에 배치하여 공장 객체를 만들고 삭제하는 것에 대해 걱정할 필요가 없습니다. 당신은 단순히 클래스를 등록하고, 컴파일러에 의해 공장 클래스가 생성되고, 공장 객체가 패턴에 의해 생성됩니다. 일생이 끝날 무렵, 모든 공장은 깨끗하게 파괴됩니다. 나는 공장의 수명을 누가 지배하는 사람에 대한 혼란이 없기 때문에 나는이 형태의 캡슐화를 좋아한다.
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top