는 (나는 아래로 더 해결책을 제시 할 ... 곰을 나와 함께 ...) (거의) 문제를 해결하기
한 가지 방법은 방문자 디자인 패턴을 사용하는 것입니다. 이런 식으로 뭔가 : 대신의 다음
class DrawVisitor
{
public:
void draw(const Shape &shape); // dispatches to correct private method
private:
void visitSquare(const Square &square);
void visitCircle(const Circle &circle);
};
:
Shape &shape = getShape(); // returns some Shape subclass
shape.draw(); // virtual method
당신은 할 것이다 :
이
DrawVisitor dv;
Shape &shape = getShape();
dv.draw(shape);
는 일반적으로 방문자 패턴이 같은 draw
방법을 구현하는 것이 :
DrawVisitor::draw(const Shape &shape)
{
shape.accept(*this);
}
하지만 Shape
계층 구조를 방문하도록 설계된 경우에만 작동합니다. 각 하위 클래스는 방문자의 적절한 visitXxxx
메서드를 호출하여 accept
가상 메서드를 구현합니다. 대부분의 경우 그렇게 설계되지 않았을 가능성이 큽니다.
(및 모든 하위 클래스)에 가상 accept
메서드를 추가하기 위해 클래스 계층을 수정할 수없는 경우 올바른 draw
메서드로 디스패치하는 다른 방법이 필요합니다. 하나의 순진한 접근 방식은 다음과 같습니다.
DrawVisitor::draw(const Shape &shape)
{
if (const Square *pSquare = dynamic_cast<const Square *>(&shape))
{
visitSquare(*pSquare);
}
else if (const Circle *pCircle = dynamic_cast<const Circle *>(&shape))
{
visitCircle(*pCircle);
}
// etc.
}
동적 인 방식을 사용하면 효과가 있습니다. 당신이 타격을 줄 수있는 경우에, 그것을 유지, 디버그를 이해하기 쉬운 간단한 방법 등
모든 형상 유형의 열거가 있었다 가정 :
enum ShapeId { SQUARE, CIRCLE, ... };
는 가상 있었다 방법 ShapeId Shape::getId() const = 0;
각 하위 클래스는 ShapeId
을 반환하도록 재정의합니다. 그렇다면 dynamic_cast
의 if-elsif-elsif 대신 큰 switch
문을 사용하여 파견을 수행 할 수 있습니다. 아니면 switch
대신 해시 테이블을 사용하십시오. 가장 좋은 경우 시나리오는이 매핑 기능을 한 곳에 두는 것이므로 매번 매핑 논리를 반복하지 않고도 여러 방문자를 정의 할 수 있습니다.
따라서 getid()
메소드가 없을 수도 있습니다. 너무 나빴어. 각 유형의 객체에 대해 고유 한 ID를 얻는 또 다른 방법은 무엇입니까? RTTI. 이것은 반드시 우아하고 완벽하지는 않지만, type_info
포인터의 해시 테이블을 만들 수 있습니다. 일부 초기화 코드에서이 해시 테이블을 빌드하거나 동적으로 (또는 둘 다) 빌드 할 수 있습니다.
DrawVisitor::init() // static method or ctor
{
typeMap_[&typeid(Square)] = &visitSquare;
typeMap_[&typeid(Circle)] = &visitCircle;
// etc.
}
DrawVisitor::draw(const Shape &shape)
{
type_info *ti = typeid(shape);
typedef void (DrawVisitor::*VisitFun)(const Shape &shape);
VisitFun visit = 0; // or default draw method?
TypeMap::iterator iter = typeMap_.find(ti);
if (iter != typeMap_.end())
{
visit = iter->second;
}
else if (const Square *pSquare = dynamic_cast<const Square *>(&shape))
{
visit = typeMap_[ti] = &visitSquare;
}
else if (const Circle *pCircle = dynamic_cast<const Circle *>(&shape))
{
visit = typeMap_[ti] = &visitCircle;
}
// etc.
if (visit)
{
// will have to do static_cast<> inside the function
((*this).*(visit))(shape);
}
}
일부 버그/구문 오류가있을 수 있지만이 예제를 컴파일하지 않았습니다. 나는 전에 이런 식으로 일을했다. 그 기술은 효과가있다. 공유 라이브러리에 문제가 생길지 모르겠다.
내가 추가 할 것입니다 마지막으로 한가지 :
class ShapeVisitor
{
public:
void visit(const Shape &shape); // not virtual
private:
virtual void visitSquare(const Square &square) = 0;
virtual void visitCircle(const Circle &circle) = 0;
};
거기에 방문하기보다는'visitCircle (const Circle & circle) '을 의미합니까? –
@Philip : 죄송합니다 ... 해결되었습니다. – Dan
흥미로운 솔루션, 나는'Visitor' 패턴 >> 패턴의 일부만을 사용하는 것이 상황에 적응되도록 의도 된 것이고 다른 방법은 아닙니다. :) –