c – 在硬件接口之间切换的最佳设计模式

我正在寻求关于我目前的方法是否有意义的建议.如果没有,我想建议一些类型的设计模式,而不是用来取代我目前的直觉.

我的前提是我有一台相机,需要一个CameraLink或CoaXPress电缆接口连接到PC的帧抓取卡.必须使用帧抓取卡控制相机和计算机之间的所有通信和数据传输,因此这两个物理硬件对象之间的耦合非常紧密.

我的问题是我想创建一个“Camera”对象(用于GUI),它具有一个“FrameGrabber”卡对象,用于获取数据和发送/接收命令和数据.但是,我有许多不同类型的不同帧抓取卡.让我们称它们为CoaxGrabberA,CoaxGrabberB,LinkGrabberA和LinkGrabberB.与LinkGrabbers相比,CoaxGrabbers需要一组不同的初始化,设置器和getter参数.

因此,我认为我需要使用两个级别的继承,但是从我读过的所有内容来看,应该很少使用继承,并且应该优先考虑组合.因此,我非常怀疑我的设计决策,并寻求某种更好的设计.这是一些半生不熟的代码的例子.它有点冗长,但重要的部分是CoaxGrabberA,CoaxGrabberB,LinkGrabberA和LinkGrabberB是FrameGrabber的孙子孙女的概念,它必须可供Camera使用.其他一切都是为了填写您可能需要的细节.

我的目标是在运行时选择我想用于Camera对象的任何framegrabber(任何make / model / interface).此外,我想轻松访问该孙子framegrabber类型所特有的所有成员函数,以在运行时修改硬件的行为.

我的问题是“是否有一个特定的设计模式来匹配我不知道的问题,这将使我的生活比使用我天真,直观的方法更容易”

//-----------------------------------------
// Parent Class
//=========================================
class FrameGrabber {
 public:
    virtual void sendCommandString(std::string cmd) = 0;
    virtual void startAcquisition() = 0;
    virtual void stopAcquisition() = 0;
};


//-----------------------------------------
// Children Classes
//=========================================
class CoaxGrabber : FrameGrabber {
 public:
    //functions unique to coax grabbers
    virtual void setCommAddress(int commAddress) = 0;   
    virtual void setStatusPort(int statusPort) = 0;    

    //functions universal to all grabbers
    virtual void sendCommandString(std::string cmd) = 0; 
    virtual void startAcquisition() = 0;                 
    virtual void stopAcquisition() = 0;  

 protected:
    int _commAddress;
    int _statusPort;        

};


class LinkGrabber : FrameGrabber {
public:
    //functions unique to link grabbers
    virtual void setBaudRate(int baudRate) = 0;
    virtual void setNumChannels(int numChannels) = 0;

    //functions universal to all grabbers
    virtual void sendCommandString(std::string cmd) = 0;    
    virtual void startAcquisition() = 0;
    virtual void stopAcquisition() = 0;

protected:
    int _baudRate;
    int _numChannels;

};


//-----------------------------------------
// Grandchildren Classes
//=========================================
class CoaxGrabberA : public CoaxGrabber {
    //identical public members as CoaxGrabber
    //different implementation using
    //different low-level API, ex: BitFlow
}


class CoaxGrabberB : public CoaxGrabber {
    //identical public members as CoaxGrabber
    //different implementation using
    //different low-level API, ex: Kaya
}


class LinkGrabberA : public LinkGrabber {
    //identical public members as LinkGrabber
    //different implementation using
    //different low-level API, ex: NationalInstruments
}


class LinkGrabberB : public LinkGrabber {
    //identical public members as LinkGrabber
    //different implementation using
    //different low-level API, ex: Imperx
}


//-----------------------------------------------------
// Finally, my Camera object, nothing too interesting here
//=====================================================
class Camera {
public:
    Camera() {
        _frameGrabber = NULL;
    }

    ~Camera() { 
        delete _frameGrabber;
    }

    void setGrabber(FrameGrabber* newGrabber)
    {
        delete _frameGrabber;
        _frameGrabber = newGrabber;
    }

    void startAcquisition() {
        _frameGrabber.startAcquisiton();
    }

    void stopAcquisition() {
        _frameGrabber.stopAcquisition();
    }

    int setSensitivity(int sens) {
        _frameGrabber.sendCommandString("sens=" + std::to_string(sens)); 
    }

private:
    FrameGrabber* _frameGrabber;

};


//-----------------------------------------
// This is why I don't like my Camera object
// the actual end-user interface smells
//=========================================
class CameraGui : QMainWindow
{
public:
    void setGrabberType(int type);
    void setCoaxGrabberCommAddress(int address);
    void setLinkGrabberBaudRate(int rate);

    CameraSystem _myCamera;
    CoaxGrabber* _myCoaxGrabber;
    LinkGrabber* _myLinkGrabber;
};


//---------------------------------------------------------------
//This function smells to me, but I cannot think of any other way
//of course, int type will be enum in actual program.
//===============================================================
void CameraGui::setGrabberType(int type) {
    switch (type) {
        case 0: 
            delete _myCoaxGrabber;
            _myCoaxGrabber = new CoaxGrabberA();
            _myCamera.setGrabber(&_myCoaxGrabber); 
            break;
        case 1: 
            delete _myCoaxGrabber;
            _myCoaxGrabber = new CoaxGrabberB();
            myCamera.setGrabber(&_myCoaxGrabber)); 
            break;
        case 2: 
            delete _myLinkGrabber;
            _myLinkGrabber = new LinkGrabberA();
            _myCamera.setGrabber(&_myLinkGrabber); 
            break;
        case 3: 
            delete _myLinkGrabber;
            _myLinkGrabber = new LinkGrabberB();
            _myCamera.setGrabber(&_myLinkGrabber); 
            break;
    }
}

//---------------------------------------------------------------
// this method of setting parameters also smells to me,
// since this data is linked to the Camera object, which
// will have no way of knowing whether the state of its
// framegrabber changed... furthermore, if I change framegrabbers,
// none of the parameter settings (state) will be remembered.
// the user will need to set them all over again.
// the only way I know to circumvent this is to allocate memory for
// every type of framegrabber, and broadcast all state changes to
// all applicable parent grabbers, which will reside in permanent
// memory until the application closes.
//===============================================================
void CameraGui::setCoaxGrabberCommAddress(int address) {
    if(myCoaxGrabber != NULL) {
        myCoaxGrabber->setCommAddress(address);
    }
}

//likewise smell
void CameraGui::setLinkGrabberBaudRate(int rate) {
    if(myLinkGrabber != NULL) {
        myLinkGrabber->setBaudRate(rate);
    }
}

任何和所有的建议将不胜感激.长话短说,我对OO设计模式知之甚少,但这感觉就像一个解决的问题,我觉得我正在重新发明轮子.是否有更好,更成熟的方式来实现我想要做的事情?

最佳答案 您的设计模式称为“工厂”,继承没有任何问题(
https://en.wikipedia.org/wiki/Factory_method_pattern)

在继承和聚合之间选择时我们应该使用的经验法则:

>如果某事反映“是”关系(例如CoaxGrabber是FrameGrabber)使用继承.
>如果某事反映出“有”关系(例如CameraGui有FrameGrabber),则使用聚合.

我建议使用智能指针(例如std :: shared_ptr)而不是new并删除当前使用的内容将使代码更易于管理且不易出错.

在这种情况下:

class Camera {
public:
    CameraSystem() {} // don't need explicit initialization

    ~CameraSystem() {} // resource in shared_ptr will be deleted automatically

    void setGrabber(const std::shared_ptr<FrameGrabber>& newGrabber)
    {
        _frameGrabber = newGrabber;
    }

    void startAcquisition() {
        _frameGrabber->startAcquisiton(); // note -> instead of .
    }

    // ....

private:
    std::shared_ptr<FrameGrabber> _frameGrabber;
};

如果使用工厂:

void CameraGui::setGrabberType(int type) {
    _myCamera.setGrabber(GrabberFactory::createGrabber(type));
}

class GrabberFactory {
public:
    std::shared_ptr<FrameGrabber> createGrabber(int type) {
        switch (type) {
        case GrabberTypeCoaxA: return {new CoaxGrabberA()};
        case GrabberTypeCoaxB: return {new CoaxGrabberB()}; 
        default: throw std::invalid_argument("Invalid grabber type");
        }
    }
};
点赞