(मैं मेरे साथ और नीचे एक समाधान प्रस्तावित कर ... भालू ...)
एक तरह से करने के लिए (लगभग) आपकी समस्या का समाधान एक आगंतुक डिजाइन पैटर्न का उपयोग करने के लिए है। कुछ इस तरह:
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
लागू करता है। सबसे अधिक संभावना है कि यह इसके लिए डिजाइन नहीं किया गया था।
Shape
के लिए एक आभासी accept
विधि जोड़ने के लिए वर्ग पदानुक्रम (और सभी उपवर्ग) को संशोधित करने में सक्षम होने के बिना, आप सही draw
विधि के लिए प्रेषण करने के लिए किसी अन्य तरीके की जरूरत है। एक naieve दृष्टिकोण यह है:
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()
विधि नहीं है। बहुत बुरा। प्रत्येक प्रकार की ऑब्जेक्ट के लिए अद्वितीय आईडी प्राप्त करने का दूसरा तरीका क्या है? 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;
};
क्या आपका मतलब है यात्रा के बजाय 'सर्किल (कॉन्स सर्किल एंड सर्कल)' यात्रा करें? –
@ फिलिप: ओह ... तय। – Dan
दिलचस्प समाधान, मुझे 'विज़िटर' पैटर्न के केवल एक हिस्से का उपयोग पसंद है >> पैटर्न को स्थिति के अनुकूल बनाया जाना है, न कि दूसरी तरफ :) –