문제

나는 프로그램에서 특정 역할을 수행하기 위해 특정 클래스가 어떻게 행동하는지 정의하는 인터페이스가있는 상황이 있지만,이 시점에서 나는 그 역할을 채우기 위해 얼마나 많은 클래스를 쓸 것인지 100% 확신하지 못합니다. . 그러나 동시에 GUI Combo/List 상자에서 사용자가 선택할 수 있기를 원합니다.이 상자는 특정 역할을 채우는 데 사용하려는 인터페이스를 구체적으로 구체적으로 구체화합니다. GUI가 사용 가능한 모든 클래스를 열거 할 수 있기를 원하지만, 그 역할을 채우기 위해 새로운 클래스를 구현하기로 결정할 때마다 (지금부터 몇 달이 지날 수 있음) 다시 돌아가서 오래된 코드를 변경하지 않아도됩니다.

내가 고려한 것들 :

  1. 열거를 사용합니다
    • 장점 :
      1. 나는 그것을하는 방법을 알고 있습니다
    • 단점
      1. 새 클래스를 추가 할 때 열거를 업데이트해야합니다.
      2. 추악한 반복
  2. 어떤 종류의 사용 static 인터페이스에 객체를 나열하고 구현 클래스의 정의 파일 내에서 새 요소 추가
    • 장점 :
      1. 오래된 코드를 변경할 필요가 없습니다
    • 단점 :
      1. 이것이 가능한지조차 확실하지 않습니다
      2. 공장 메소드가 적절한 생성자를 선택할 수 있도록 어떤 종류의 정보를 저장 해야하는지 확실하지 않습니다 (문자열과 인터페이스의 객체로 포인터를 반환하는 함수 포인터 사이의 맵이있을 수 있음).

나는 이것이 더 숙련 된 프로그래머가 아마도 (그리고 종종) 이전에 왔을 것이라는 문제 (또는 문제와 유사한) 문제라고 생각합니다. 아마도 이런 종류의 문제에 대한 일반적인 해결책이있을 것입니다. 'M을 생각해 낼 수 있습니다. 그래서 어떻게해야합니까?

(PS를 검색했지만 내가 찾은 것은 이것뿐만 아니라 동일하지 않습니다. 일반 인터페이스를 구현하는 모든 항목을 어떻게 열거합니까?. 그는 이미 알아 내려고하는 문제를 해결하는 방법을 이미 알고있는 것 같습니다.)

편집 : 나는 "어떻게 열거 할 수 있는가 ... '정말 관심이있는 것은 컴파일 타임 부기입니다.

도움이 되었습니까?

해결책

제작자 기능에 대한 포인터로 수업을 등록 할 수있는 싱글 톤을 만듭니다. 콘크리트 클래스의 CPP 파일에서 각 클래스를 등록합니다.
이 같은:

class Interface;
typedef boost::function<Interface* ()> Creator;

class InterfaceRegistration
{
    typedef map<string, Creator> CreatorMap;
public:
    InterfaceRegistration& instance() {  
        static InterfaceRegistration interfaceRegistration;
        return interfaceRegistration;
    }

    bool registerInterface( const string& name, Creator creator )
    {
        return (m_interfaces[name] = creator);
    }

    list<string> names() const
    {
        list<string> nameList;  
        transform(
            m_interfaces.begin(), m_interfaces.end(), 
            back_inserter(nameList) 
            select1st<CreatorMap>::value_type>() );
    }

    Interface* create(cosnt string& name ) const 
    { 
        const CreatorMap::const_iterator it 
            = m_interfaces.find(name);  
        if( it!=m_interfaces.end() && (*it) )
        {
            return (*it)();
        }
        // throw exception ...
        return 0;
    }

private:
    CreatorMap m_interfaces;
};


// in your concrete classes cpp files
namespace {
bool registerClassX = InterfaceRegistration::instance("ClassX", boost::lambda::new_ptr<ClassX>() );
}

ClassX::ClassX() : Interface()
{
    //....
}

// in your concrete class Y cpp files
namespace {
bool registerClassY = InterfaceRegistration::instance("ClassY", boost::lambda::new_ptr<ClassY>() );
}

ClassY::ClassY() : Interface()
{
    //....
}

다른 팁

나는 몇 년 전에 이와 비슷한 일을하는 것을 모호하게 기억합니다. 당신의 선택 (2)은 내가 한 일입니다. 이 경우 그것은 a std::mapstd::string 에게 std::typeinfo. 각각 .cpp 파일에서 다음과 같은 클래스를 등록했습니다.

static dummy = registerClass (typeid (MyNewClass));

registerClass a type_info 객체와 단순히 돌아옵니다 true. 이를 확인하려면 변수를 초기화해야합니다 registerClass 시작 시간 동안 호출됩니다. 단순히 전화 registerClass 글로벌 네임 스페이스에서는 오류가 있습니다. 그리고 만들기 dummy STATIC을 사용하면 이름 충돌없이 컴파일 장치에서 이름을 재사용 할 수 있습니다.

나는 TIMW의 답변에 설명 된 것과 유사한 자체 등록 클래스 공장을 구현하기 위해이 기사를 언급했지만 템플릿 공장 프록시 클래스를 사용하여 객체 등록을 처리하는 좋은 트릭이 있습니다. 볼만한 가치가 있습니다 :)

c ++ ->의 자체 등록 객체 http://www.ddj.com/184410633

편집하다

다음은 내가 한 테스트 앱입니다 (조금 정리;) :

Object_factory.h

#include <string>
#include <vector>
// Forward declare the base object class
class Object;
// Interface that the factory uses to communicate with the object proxies
class IObjectProxy {
public:
    virtual Object* CreateObject() = 0;
    virtual std::string GetObjectInfo() = 0;
};
// Object factory, retrieves object info from the global proxy objects
class ObjectFactory {
public:
    static ObjectFactory& Instance() {
        static ObjectFactory instance;
        return instance;
    }
    // proxies add themselves to the factory here 
    void AddObject(IObjectProxy* object) {
        objects_.push_back(object);
    }
    size_t NumberOfObjects() {
        return objects_.size();
    }
    Object* CreateObject(size_t index) {
        return objects_[index]->CreateObject();
    }
    std::string GetObjectInfo(size_t index) {
        return objects_[index]->GetObjectInfo();
    }

private:
    std::vector<IObjectProxy*> objects_;
};

// This is the factory proxy template class
template<typename T>
class ObjectProxy : public IObjectProxy {
public:
    ObjectProxy() {
        ObjectFactory::Instance().AddObject(this);
    }        
    Object* CreateObject() {
        return new T;
    }
    virtual std::string GetObjectInfo() {
        return T::TalkToMe();
    };    
};

Objects.h

#include <iostream>
#include "object_factory.h"
// Base object class
class Object {
public:
    virtual ~Object() {}
};
class ClassA : public Object {
public:
    ClassA() { std::cout << "ClassA Constructor" << std::endl; }
    ~ClassA() { std::cout << "ClassA Destructor" << std::endl; }
    static std::string TalkToMe() { return "This is ClassA"; }
};
class ClassB : public Object {
public:
    ClassB() { std::cout << "ClassB Constructor" << std::endl; }
    ~ClassB() { std::cout << "ClassB Destructor" << std::endl; }
    static std::string TalkToMe() { return "This is ClassB"; }
};

Objects.cpp

#include "objects.h"
// Objects get registered here
ObjectProxy<ClassA> gClassAProxy;
ObjectProxy<ClassB> gClassBProxy;

main.cpp

#include "objects.h"
int main (int argc, char * const argv[]) {
    ObjectFactory& factory = ObjectFactory::Instance();
    for (int i = 0; i < factory.NumberOfObjects(); ++i) {
        std::cout << factory.GetObjectInfo(i) << std::endl;
        Object* object = factory.CreateObject(i);
        delete object;
    }
    return 0;
}

산출:

This is ClassA
ClassA Constructor
ClassA Destructor
This is ClassB
ClassB Constructor
ClassB Destructor

Windows에 있고 C ++/CLI를 사용하는 경우 매우 쉬워집니다. .NET 프레임 워크는 반사를 통해이 기능을 제공하며 관리 코드에서 매우 깨끗하게 작동합니다.

기본 C ++에서는 라이브러리를 쿼리하거나 런타임 정보에 대한 응용 프로그램을 쿼리하는 간단한 방법이 없기 때문에 조금 까다로워집니다. 많이있다 이것을 제공하는 프레임 워크 (IOC, DI 또는 플러그인 프레임 워크를 찾으십시오) 그러나 가장 간단한 수단은 공장 메소드가 스스로 등록하는 데 사용할 수 있고 특정 기본 클래스의 구현을 반환 할 수있는 어떤 형태의 구성을 갖는 것입니다. DLL로드를 구현하고 공장 방법을 등록하면됩니다. 일단 있으면 매우 쉽습니다.

당신이 고려할 수있는 것은 객체 카운터입니다. 이렇게하면 할당하는 모든 장소뿐만 아니라 구현 정의 만 변경할 필요가 없습니다. 공장 솔루션의 대안입니다. 장단점을 고려하십시오.

그렇게하는 우아한 방법은 CRTP : 호기심으로 되풀이되는 템플릿 패턴. 주요 예는 그런 카운터입니다 :)

이렇게하면 콘크리트 클래스 구현을 추가하면됩니다.

class X; // your interface

class MyConcreteX : public counter<X>
{
    // whatever
};

물론 마스터하지 않은 외부 구현을 사용하는 경우 적용 할 수 없습니다.

편집하다:

정확한 문제를 처리하려면 첫 번째 인스턴스 만 계산하는 카운터가 있어야합니다.

내 2 센트

(기본) C ++에서 클래스의 서브 클래스를 쿼리하는 방법은 없습니다. 인스턴스를 어떻게 만드나요? 공장 방법을 사용하여 작업중인 모든 서브 클래스를 반복 할 수 있습니다. 이와 같은 인스턴스를 만들면 나중에 새 서브 클래스를 추가하는 것을 잊을 수 없습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top