不同版本的设备的代码结构

时间:2019-05-20 07:08:28

标签: c++ architecture version driver

我正在编写一个“设备驱动程序”(C ++ 14),它可以处理用于不同版本设备的协议的多个版本。该设备驱动程序在外部PC上运行,该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的回答,我将ProtocolBaseProtocolV2修改为:

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了,尽管基类必须知道所有功能。这似乎是目前最好的解决方案。

2 个答案:

答案 0 :(得分:0)

通常,这取决于派生类ProtocolV1ProtocolV2的形成方式以及数据成员和天气(如果各自的成员函数是否要使用不同的数据成员)!

原因很简单,因为不依赖于成员数据,成员函数仅对调用它们的对象的类型敏感,而对它们的值/状态不敏感

就像(非成员)函数重载一样:

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样式强制转换: 结果是 SAME

最后,如果您要调用成员函数然后从中调用另一个函数,该函数需要一些数据成员,那么在各个派生类中,数据成员之间是不同的,那么您就不能使用任何强制类型转换,除非您引入某种形式的补偿来填充强制类型中未显示的数据!

祝你好运!

答案 1 :(得分:0)

就像在大多数情况下选择适当的系统架构一样,答案是 “这取决于” :)。最舒适的解决方案是引入特定于协议的行为 for ($i = 110009; $i <= 114993; $i++) { echo $i . "\n"; } 子类在其构造函数中的位置

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()作为指向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; } }; 会实现此行为 作为协议的有效部分。

最后,如果以上都不是,您当然可以按照建议的方式使用dynamic_cast。 我个人会尽量避免使用ProtocolV2,因为我的办公室同事肯定会开始滥用它, 但在某些情况下,这是一个正确的解决方案。

此外,如果您决定强制转换指针,请将dynamic_caststd::shared_ptr一起使用,而不要从dynamic_pointer_cast访问原始指针。