不同版本设备的代码结构
Code structure for different versions of devices
我正在编写一个 "device driver" (C++14),它可以处理针对不同版本设备的多个协议版本。此设备驱动程序位于外部 PC 上 运行,它使用基于 HTTP 的协议通过以太网与设备通信。所有版本都有共同的功能,但某些功能可能在某些版本的协议中是附加的。
下面是一个例子:
class ProtocolBase {
public:
virtual void reset_parameters() {
std::cout << "reset parameters" << std::endl;
}
virtual void set_parameters() {
std::cout << "set parameters" << std::endl;
}
};
class ProtocolV1 : public ProtocolBase
{
public:
void set_parameters() override {
std::cout << "set parameters for V1" << std::endl;
}
};
class ProtocolV2 : public ProtocolBase
{
public:
void set_parameters() override {
std::cout << "set parameters for V2" << std::endl;
}
void reset_parameters() {
std::cout << "reset parameters for V2" << std::endl;
}
void do_V2() {
std::cout << "doing V2" << std::endl;
}
};
下面是main
:
int main(int argc, char const *argv[])
{
int version = std::atoi(argv[1]);
std::unique_ptr<ProtocolBase> protocol = std::make_unique<ProtocolV1>();
switch (version)
{
case 1:
/* do nothing at the moment */
break;
case 2:
protocol.reset(new ProtocolV2);
break;
default:
break;
}
protocol->reset_parameters();
if(ProtocolV2* p = dynamic_cast<ProtocolV2*>(protocol.get())) { //not sure about this
p->do_V2();
}else {
std::cout << "This functionality is unavailable for this device" << std::endl;
}
protocol->set_parameters();
return 0;
}
我觉得使用 dynamic_cast
并不是去这里的最佳方式。期待一些反馈。
编辑: 根据@Ptaq666 的回答,我将 ProtocolBase
和 ProtocolV2
修改为:
class ProtocolBase {
public:
virtual void do_V(){
std::cerr << "This functionality is unavailable for this device" << std::endl;
}
};
class ProtocolV2 : public ProtocolBase
{
public:
void do_V() override {
std::cout << "doing V2" << std::endl;
}
};
有了这个,就不再需要 dynamic_cast
,尽管 base class 必须知道所有功能。这似乎是目前最好的解决方案。
一般要看派生的类 ProtocolV1
和ProtocolV2
是怎么形成的,数据成员是什么,如果各自的成员函数要使用不同的数据,天气如何会员与否!
原因很简单,因为不依赖于成员数据,成员函数只对调用它们的对象的类型敏感,而不是它们的value/state!
这就像有一个(非成员)函数重载,如:
void function(ProtocolV1 *){
std::cout << "set parameters for V1" << std::endl;
}
void function(ProtocolV2 *){
std::cout << "set parameters for V2" << std::endl;
}
然后用 ProtocolV1 *
类型的指针调用一次,用 ProtocolV2 *
.
类型的空指针调用一次
如果您喜欢您在问题中提出的用法的替代方案,您甚至可以使用 C-style 强制转换:
结果是 SAME!
最后,如果您要调用成员函数,然后从中调用另一个需要一些数据的函数 member/s,is/are 不同派生的 类 作为它的 argument/s,那么你不能使用任何强制转换,除非你引入某种形式的补偿来填充未在强制转换类型中呈现的数据!
祝你好运!
在大多数情况下,当谈到选择合适的系统架构时,答案是
"it depends" :)。最舒适的解决方案是引入 protocol-specific 行为
ProtocolBase
个子类的构造函数
class ProtocolV2 : public ProtocolBase
{
public:
ProtocolV2::ProtocolV2(args) {
// set some params that will determine when do_V2() is called
// it can be some numeric setting, a callback, or similar
}
void set_parameters() override {
// you can use V2 behavior here maybe?
std::cout << "set parameters for V2" << std::endl;
}
void reset_parameters() override {
// or here maybe?
std::cout << "reset parameters for V2" << std::endl;
}
private:
void do_V2() {
std::cout << "doing V2" << std::endl;
}
};
如果由于某种原因您不能这样做,可以将 do_V2()
保留为 public
non-virtual 方法,但在将 ProtocolV2
作为指向 ProtocolBase
的指针传递之前调用它
到将使用它的系统。当然限制是do_V2
只能在外面调用
您的系统范围,这可能无法真正解决问题。
另一种选择是将do_V2()
实际移动到界面:
class ProtocolBase {
public:
virtual void reset_parameters() {
std::cout << "reset parameters" << std::endl;
}
virtual void set_parameters() {
std::cout << "set parameters" << std::endl;
}
virtual void do_V2() {
std::cout << "not supported" << std::endl;
}
};
并默认将其实现为 "not supported" 行为。只有 ProtocolV2
会实现此行为
作为协议的有效部分。
最后,如果上面的none没问题,那你当然可以按照你的建议使用dynamic_cast。
我个人尽量避免 dynamic_cast
因为我的同事肯定会开始滥用它,
但在某些情况下这是一个正确的解决方案。
此外,如果您决定转换指针,请将 std::shared_ptr
与 dynamic_pointer_cast
一起使用,而不是从 unique_ptr
.
访问原始指针
我正在编写一个 "device driver" (C++14),它可以处理针对不同版本设备的多个协议版本。此设备驱动程序位于外部 PC 上 运行,它使用基于 HTTP 的协议通过以太网与设备通信。所有版本都有共同的功能,但某些功能可能在某些版本的协议中是附加的。
下面是一个例子:
class ProtocolBase {
public:
virtual void reset_parameters() {
std::cout << "reset parameters" << std::endl;
}
virtual void set_parameters() {
std::cout << "set parameters" << std::endl;
}
};
class ProtocolV1 : public ProtocolBase
{
public:
void set_parameters() override {
std::cout << "set parameters for V1" << std::endl;
}
};
class ProtocolV2 : public ProtocolBase
{
public:
void set_parameters() override {
std::cout << "set parameters for V2" << std::endl;
}
void reset_parameters() {
std::cout << "reset parameters for V2" << std::endl;
}
void do_V2() {
std::cout << "doing V2" << std::endl;
}
};
下面是main
:
int main(int argc, char const *argv[])
{
int version = std::atoi(argv[1]);
std::unique_ptr<ProtocolBase> protocol = std::make_unique<ProtocolV1>();
switch (version)
{
case 1:
/* do nothing at the moment */
break;
case 2:
protocol.reset(new ProtocolV2);
break;
default:
break;
}
protocol->reset_parameters();
if(ProtocolV2* p = dynamic_cast<ProtocolV2*>(protocol.get())) { //not sure about this
p->do_V2();
}else {
std::cout << "This functionality is unavailable for this device" << std::endl;
}
protocol->set_parameters();
return 0;
}
我觉得使用 dynamic_cast
并不是去这里的最佳方式。期待一些反馈。
编辑: 根据@Ptaq666 的回答,我将 ProtocolBase
和 ProtocolV2
修改为:
class ProtocolBase {
public:
virtual void do_V(){
std::cerr << "This functionality is unavailable for this device" << std::endl;
}
};
class ProtocolV2 : public ProtocolBase
{
public:
void do_V() override {
std::cout << "doing V2" << std::endl;
}
};
有了这个,就不再需要 dynamic_cast
,尽管 base class 必须知道所有功能。这似乎是目前最好的解决方案。
一般要看派生的类 ProtocolV1
和ProtocolV2
是怎么形成的,数据成员是什么,如果各自的成员函数要使用不同的数据,天气如何会员与否!
原因很简单,因为不依赖于成员数据,成员函数只对调用它们的对象的类型敏感,而不是它们的value/state!
这就像有一个(非成员)函数重载,如:
void function(ProtocolV1 *){
std::cout << "set parameters for V1" << std::endl;
}
void function(ProtocolV2 *){
std::cout << "set parameters for V2" << std::endl;
}
然后用 ProtocolV1 *
类型的指针调用一次,用 ProtocolV2 *
.
如果您喜欢您在问题中提出的用法的替代方案,您甚至可以使用 C-style 强制转换: 结果是 SAME!
最后,如果您要调用成员函数,然后从中调用另一个需要一些数据的函数 member/s,is/are 不同派生的 类 作为它的 argument/s,那么你不能使用任何强制转换,除非你引入某种形式的补偿来填充未在强制转换类型中呈现的数据!
祝你好运!
在大多数情况下,当谈到选择合适的系统架构时,答案是
"it depends" :)。最舒适的解决方案是引入 protocol-specific 行为
ProtocolBase
个子类的构造函数
class ProtocolV2 : public ProtocolBase
{
public:
ProtocolV2::ProtocolV2(args) {
// set some params that will determine when do_V2() is called
// it can be some numeric setting, a callback, or similar
}
void set_parameters() override {
// you can use V2 behavior here maybe?
std::cout << "set parameters for V2" << std::endl;
}
void reset_parameters() override {
// or here maybe?
std::cout << "reset parameters for V2" << std::endl;
}
private:
void do_V2() {
std::cout << "doing V2" << std::endl;
}
};
如果由于某种原因您不能这样做,可以将 do_V2()
保留为 public
non-virtual 方法,但在将 ProtocolV2
作为指向 ProtocolBase
的指针传递之前调用它
到将使用它的系统。当然限制是do_V2
只能在外面调用
您的系统范围,这可能无法真正解决问题。
另一种选择是将do_V2()
实际移动到界面:
class ProtocolBase {
public:
virtual void reset_parameters() {
std::cout << "reset parameters" << std::endl;
}
virtual void set_parameters() {
std::cout << "set parameters" << std::endl;
}
virtual void do_V2() {
std::cout << "not supported" << std::endl;
}
};
并默认将其实现为 "not supported" 行为。只有 ProtocolV2
会实现此行为
作为协议的有效部分。
最后,如果上面的none没问题,那你当然可以按照你的建议使用dynamic_cast。
我个人尽量避免 dynamic_cast
因为我的同事肯定会开始滥用它,
但在某些情况下这是一个正确的解决方案。
此外,如果您决定转换指针,请将 std::shared_ptr
与 dynamic_pointer_cast
一起使用,而不是从 unique_ptr
.