我正在写一个工具,使用户可以通过更改设置然后流传输信息来与一些硬件进行交互。
为此,我有几个正在运行的线程:EquipmentInterface
和DataProcessor
通过Queue
连接。EquipmentInterface
线程具有更改设备设置(例如Rotate
和Refocus
)的方法,并将结果信息(CurrentAngle
和CurrentFocalDistance
)添加到Queue
中。一旦设置正确,就可以使用StartStreaming
和StopStreaming
的方法,一旦开始流传输,来自设备的数据将被打包并添加到队列中。
放在队列中的所有信息均来自单个BaseMessage
类,该类包括消息类型的指示。然后,我获得了 Angular ,焦距,流的开始和结束以及当然数据本身的消息类型。DataProcessor
侦听队列的另一端,并根据当前 Angular /焦距来处理后续数据。
现在,问题是,我在数据处理器中有一个函数,该函数使用switch语句对传入的消息进行类型检查。然后将这些消息向下转换为适当的类型,并传递给适当的处理程序。实际上,不仅有一个DataProcessor在侦听单个队列,而且实际上在多个队列上有多个侦听器(有些存储在磁盘上,有些则在gui上显示信息)。每次添加一些信息时,我都必须创建一个新的BaseMessage
派生类,向该基类添加一个新类型,然后更新每个使用者中的switch语句以应对新消息。
我觉得关于这种体系结构的某些事情是错误的,最近我阅读了很多有关向下转换的内容。从我所看到的,普遍的共识似乎是what I'm doing is a bad code smell。我见过a suggestion which use Boost,但是它们看起来比我的switch语句还干净(也许我遗漏了什么?)。
所以我的问题是:我应该尝试避免使用交换语句/向下转换解决方案吗?
我的实现是在C++ / CLI中进行的,因此我追求的是.net或C++解决方案。
编辑-根据iammilind和stfaanv的评论,这是您所建议的事情:
class QueuedItem
{
public:
QueuedItem() { }
virtual ~QueuedItem() { }
};
class Angle : public QueuedItem
{
public:
Angle() {}
virtual ~Angle() { }
};
class FocalLength : public QueuedItem
{
public:
FocalLength() {}
virtual ~FocalLength() { }
private:
};
class EquipmentHandler
{
protected:
virtual void ProcessAngle(Angle* angle) {};
virtual void ProcessFocalLength(FocalLength* focalLength) {};
public:
void ProcessMessages(QueuedItem* item)
{
Angle* pAngle = dynamic_cast<Angle*>(item);
if( pAngle != NULL )
{
ProcessAngle(pAngle);
}
FocalLength* pFocalLength = dynamic_cast<FocalLength*>(item);
if( pFocalLength != NULL )
{
ProcessFocalLength(pFocalLength);
}
}
};
class MyDataProcessor : public EquipmentHandler
{
protected:
virtual void ProcessAngle(Angle* angle) override { printf("Processing Angle"); }
virtual void ProcessFocalLength(FocalLength* focalLength) override { printf("Processing FocalLength"); };
};
int _tmain(int argc, _TCHAR* argv[])
{
// Equipment interface thread...
FocalLength* f = new FocalLength();
QueuedItem* item = f; // This gets stuck onto the queue
// ...DataProcessor thread (after dequeuing)
QueuedItem* dequeuedItem = item;
// Example of a DataProcessor implementation.
// In reality, this would
MyDataProcessor dataProc;
dataProc.ProcessMessages(dequeuedItem);
return 0;
}
...并且可以简化吗?
ProcessMessages
有点笨拙,但这是我可以看到的唯一方法,而无需在基础类中使用switch语句和某种枚举的消息类型标识符。 最佳答案
您可以尝试访客设计模式:http://en.wikipedia.org/wiki/Visitor_pattern
每个DataProcessor都将从BaseVisitor
类继承,该类定义用于处理每种Message的虚拟方法。基本上,这些方法只是noop。
定义新的消息类型时,您需要在BaseVisitor
中添加一个新的虚拟方法,该方法具有此消息类型的noop实现。然后,如果子DataProcessor
类要处理此消息类型,则仅在此DataProcessor
中覆盖虚拟方法。其他所有DataProcessor
保持不变。
#include <iostream>
class FocalLength;
class Angle;
class EquipmentVisitor;
class QueuedItem
{
public:
QueuedItem() { }
virtual ~QueuedItem() { }
virtual void AcceptVisitor(EquipmentVisitor& visitor) = 0;
};
class EquipmentVisitor
{
public:
virtual ~EquipmentVisitor() {}
virtual void Visit(FocalLength& item) {}
virtual void Visit(Angle& item) {}
void ProcessMessages(QueuedItem* item)
{
item->AcceptVisitor(*this);
}
};
class Angle : public QueuedItem
{
public:
Angle() {}
virtual ~Angle() { }
void AcceptVisitor(EquipmentVisitor& visitor) { visitor.Visit(*this); }
};
class FocalLength : public QueuedItem
{
public:
FocalLength() {}
virtual ~FocalLength() { }
void AcceptVisitor(EquipmentVisitor& visitor) { visitor.Visit(*this); }
private:
};
class MyDataProcessor : public EquipmentVisitor
{
public:
virtual ~MyDataProcessor() {}
void Visit(Angle& angle) { std::cout << "Processing Angle" << std::endl; }
void Visit(FocalLength& focalLength) { std::cout << "Processing FocalLength" << std::endl; }
};
int main(int argc, char const* argv[])
{
// Equipment interface thread...
FocalLength* f = new FocalLength();
QueuedItem* item = f; // This gets stuck onto the queue
// ...DataProcessor thread (after dequeuing)
QueuedItem* dequeuedItem = item;
// Example of a DataProcessor implementation.
// In reality, this would
MyDataProcessor dataProc;
dataProc.ProcessMessages(dequeuedItem);
return 0;
}
关于c++ - 通过队列传递信息时,如何避免向下转换?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10101443/