처음부터 질문 제목을 이해하지 못한다면 그것은 당신의 잘못이 아닙니다. 더 나은 설명을 생각할 수 없었습니다. 다음은 문제에 대한 설명입니다. 다소 시간이 걸릴 수 있으므로 미리 사과드립니다.
내 프로그램의 초기 버전에는 Ecosystem 클래스와 Individual 클래스가있었습니다.
// Very simplified, for illustration purposes
class Ecosystem
{
protected:
// The int is just the ID of the individual.
std::map<int, std::shared_ptr<Individual> > individuals;
public:
Ecosystem();
void func(int _individual_id)
{
std::cout << "Individual's age: "
<< individuals[_individual_id]->get_age()
<< std::endl;
}
void routine(int _individual_id)
{
// Another function working via
// the pointers in individuals.
}
// More such functions...
};
class Individual
{
protected:
int age;
public:
Individual();
inline int get_age() const
{
return age;
}
};
Ecosystem 클래스에는 수십 개의 기능이 포함되어 있으며 앞으로 더 많은 기능을 추가 할 것입니다.
이제 Individual 클래스를 기본 클래스와 TypeAIndividual 및 TypeBIndividual과 같은 두 개의 파생 클래스로 분할하기로 결정했습니다. 각각 다른 클래스에 필요하지 않은 멤버와 특성이 있기 때문입니다 (기본 클래스를 통해 몇 개의 멤버와 특성도 공유하기 때문입니다). ). 따라서 기본 Individual 클래스와 두 개의 파생 클래스가 있습니다.
class TypeAIndividual : public Individual
{
protected:
// Data structures specific to individuals of type A
public:
TypeAIndividual();
};
class TypeBIndividual : public Individual
{
protected:
// Data structures specific to individuals of type B
public:
TypeBIndividual();
};
문제는 생태계가 이제 TypeAEcosystem과 TypeBEcosystem으로 분리되어야한다는 것입니다.
class Ecosystem
{
protected:
// Holding pointers to the base Individual class is pointless (pun not intended)
// std::map<int, std::shared_ptr<Individual> > individuals;
public:
Ecosystem();
// I want to keep func() in the base class
// because it only accesses attributes and
// members common to both classes derived
// from Individual.
void func(int _individual_id)
{
// Hmmmm...
// The pointers don't live in the Ecosystem class any more!
std::cout << "Individual's age: "
<< individuals[_individual_id]->get_age()
<< std::endl;
}
// OK to implement in each class
// derived from Ecosystem.
virtual void routine(int _individual_id) = 0;
};
class TypeAEcosystem : public Ecosystem
{
protected:
// Pointers to individuals
// of the corresponding type.
std::map<int, std::shared_ptr<TypeAIndividual> > individuals;
public:
TypeAEcosystem();
// Reimplementing routine() is OK
// because it does things specific to
// this individual type.
virtual void routine (int _individual_id)
{
// Operate on data structures particular
// to this type of individual.
}
};
class TypeBEcosystem : public Ecosystem
{
protected:
// Pointers to individuals
// of the corresponding type.
std::map<int, std::shared_ptr<TypeBIndividual> > individuals;
public:
TypeBEcosystem();
// Reimplementing routine() is OK
// because it does things specific to
// this individual type.
virtual void routine (int _individual_id)
{
// Operate on data structures particular
// to this type of individual.
}
};
TypeAEcosystem과 TypeBEcosystem은 모두 void func(int _individual_id)
해당 유형의 개인에 액세스 해야하는를 사용 합니다. 그러나 기본 클래스 Ecosystem은 더 이상 개인에 대한 포인터를 포함하지 않습니다. std::map
s는 기본 클래스가 아닌 각 파생 클래스에 있기 때문 입니다.
내 질문은 : 생태계에서 파생 된 각 클래스에서 별도의 구현을 피하면서 적절한 유형의 개인 ( TypeAIndividual
또는 TypeBIndividual
)에 액세스하려면 어떻게 void func(int _individual_id)
해야합니까? 즉, func()
변경시 파생 클래스를 변경할 필요가 없도록 기본 클래스 를 유지하는 방법이 있습니까? 실제 프로그램에는 하나만 매개 변수로 사용하는 수십 개의 함수 func()
가 있습니다 int
. 또한 이러한 함수 중 일부는 Ecosystem
클래스의 다른 구조에서 개별 ID를 가져 오므로 단순히 TypeAIndividual
또는에 대한 포인터를 전달할 수 없습니다 TypeBIndividual
.
두 파생 클래스에 필요한 모든 데이터 구조 를 사용하여 공통 클래스 로 병합 TypeAIndividual
하고 TypeBIndividual
다시 되돌립니다 Individual
. 이것은 특히 서투른 일을하는 방법으로 생각되지만 적어도 작동합니다.
제작 func()
및 (주) 가상과에서 구현 TypeAEcosystem
하고 TypeBEcosystem
. 즉, 기능 중 하나를 변경하려면 두 구현을 모두 변경해야합니다 (= 유지 관리의 악몽).
다음 과 같이 두 가지 유형의 개인 Ecosystem
을 보유하는 클래스가 하나뿐입니다 std::map
.
// Seems clunky...
class Ecosystem
{
protected:
// Note: The Ecosystem can contain
// one OR the other, but not both!
// One map will always be empty.
std::map<int, std::shared_ptr<TypeAIndividual> > type_a_individuals;
std::map<int, std::shared_ptr<TypeBIndividual> > type_b_individuals;
public:
Ecosystem();
void func(int _individual_id)
{
// Check what type of individuals we
// are working with and operate on the
// appropriate container.
if (type_a_individuals.size() > 0)
{
std::cout << "Individual's age: "
<< type_a_individuals[_individual_id]->get_age()
<< std::endl;
}
else
{
std::cout << "Individual's age: "
<< type_b_individuals[_individual_id]->get_age()
<< std::endl;
}
}
};
이를 위해서는 모든 함수에 검사를 삽입해야하는데, 이는 별도의 클래스에 함수를 갖는 것만큼이나 유지 관리 측면에서 나쁘다.
참고 : 포인터 전달을 피하고 싶지만 문제가 해결되면 업 캐스팅 및 / 또는 다운 캐스팅을 적절하게 고려할 것입니다 (최후의 수단으로 ...).
어떤 제안이라도 환영합니다!
환상적인 답변에 감사드립니다! amit과 Chris가 제안한 것처럼 내 Ecosystem
수업을 보았고 충분히 확실하게 너무 부피가 커졌습니다. 멤버 함수를 다른 클래스로 옮겼고 이제 클래스에서 4 ~ 5 개의 필수 함수로 축소되었습니다 Ecosystem
. 이 Ecosystem
클래스는 라이브러리에 상주하고 개인과 함께 실험을 수행 할 수있는 인터페이스를 제공하지만 사용자가 Individual
s 및 기타 클래스를 직접 조작 할 수있는 것을 원하지 않으므로 완전히 제거 할 수는 없습니다.
나는 모든 제안을 좋아했고 몇 가지 독창적 인 해결책이 있습니다. 즉, Chris가 제안한 것은 매우 깔끔하고 세 개의 개별 클래스 (기본 및 두 개의 파생 클래스)가 아닌 단일 생태계 클래스를 가질 수 있다는 점에서 즉시 저의 관심을 끌었습니다. 개인의 유형은 구성 파일에 지정할 수 있으며 동일한 실험 내에서 서로 다른 구성 파일에서 여러 생태계를 생성 할 수 있습니다. 이것은 받아 들여지는 대답입니다.
건설적인 의견을 보내 주신 모든 분들께 다시 한 번 감사드립니다!
내 의견에서 이미 말했듯이 생태계를 템플릿 클래스로 만들고 각 IndivualType에 대해 하나의 생태계 인스턴스를 가질 수 있습니다.
template <class IndivualType>
class Ecosystem {
protected:
// The int is just the ID of the individual.
std::map<int, std::shared_ptr<IndivualType> > individuals;
public:
// ...
};
주어진 IndividualType에 대해 생태계가 다르게 작동해야하는 경우 다음과 같이 명시 적으로 생태계를 전문화 할 수 있습니다.
template <>
class Ecosystem<SpecialIndividualType> {
protected:
// The int is just the ID of the individual.
std::map<int, std::shared_ptr<SpecialIndividualType> > individuals;
public:
// special implementation for EcoSystem for SpecialIndividualType
};
이것은 아마도 필요하지 않을 수도 있지만 알아두면 좋을 것입니다.
마지막으로 말했듯이
Ecosystem 클래스에는 수십 개의 기능이 포함되어 있으며 앞으로 더 많은 기능을 추가 할 것입니다.
생태계의 기능을 정책으로 분할하는 것을 고려할 수 있습니다. 나는 당신의 필요를 모르지만 예를 들어 :
template <class IndivualType, class SomePolicy1, class SomePolicy2>
class Ecosystem {
private:
const SomePolicy1 mSp1;
const SomePolicy2 mSp2;
protected:
// The int is just the ID of the individual.
std::map<int, std::shared_ptr<IndivualType> > individuals;
public:
Ecosystem (const SomePolicy1& sp1= SomePolicy1(), const SomePolicy2& sp2= SomePolicy2())) : mSp1(sp1), mSp2(sp2) {}
// ...
void func(int _individual_id)
mSp1.doSmth(_individual_id);
}
void func2(int _individual_id) {
mSp2.doSmth(_individual_id);
}
};
이를 "정책 기반 설계"라고하며 웹에서 이에 대한 많은 정보를 찾을 수 있습니다.
물론 이미 언급 한 방법을 가상으로 만드는 것과 같은 다른 솔루션도 있습니다. 나는 아마도 (당신이 가진 시간에 따라) 둘 다 시도하고 당신이 가장 편안하다고 느끼는 것을 볼 것입니다.
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다