设备数据链路的设计模式

Design pattern for device data link

本文关键字:设计模式 数据链路      更新时间:2023-10-16

我有三种类型的设备(USB、COM和无线)和一个用于连接它们的PC软件。每个设备都具有从RS485网络连接、读取和写入数据的功能。在PC软件上,我必须实现应用层类才能使用设备。我正在寻找一些设计模式来编写应用层和传输层之间的连接。第一个想法是编写抽象类DataLink,每个设备都将继承抽象类接口(纯OOP):

class DataLink {
    public:
    virtual bool read() = 0;
    virtual bool write() = 0;
};
class USBDevice : public DataLink {
    public:
    bool read() { /* some code */ }
    bool write() { /* some code */ }
    bool specificUSBFunction() { /* some code */ }
};
class COMDevice : public DataLink {
    public:
    bool read() { /* some code */ }
    bool write() { /* some code */ }
    bool specificCOMFunction(){ /* some code */ } 
};
DataLink *dl = new COMDevice();
dl->read();
dl->write();

现在,如果我想使用特定的USB或COM功能,我必须使用丑陋的铸造。另一个问题是,这个类必须是singleton,因为我们只有一个可用的设备,所以我们不能创建多个对象。我正在寻找一种使用C++(可以是v11或v14)实现这一点的好方法。

首先,由于您有一个抽象类,我强烈建议您考虑定义一个抽象构造函数。

class DataLink {
public:
    virtual bool read() = 0;
    virtual bool write() = 0;
    virtual ~DataLink() {}
};

现在创建这些设备引发了一些问题。你的多态设计更倾向于使用参数化的工厂方法,其中参数(配置数据?)会告诉你是要创建COM、USB还是WIFI设备:

DataLink *dl = CreateDevice("COM");   // For example. COuld use an enum as well

但你增加了另一个限制:

这个类必须是singleton,因为我们有只有一个设备可用,因此我们无法创建多个对象。

事实上,singleton的目的不仅是确保单个实例,而且还确保对它的全局访问点。如果你不需要这样的全局访问,我强烈建议不要在这里使用singleton。

顺便说一句,你的限制提出了其他问题:你有每种类型的设备吗?或者你有一个设备,不管它是什么类型的?最重要的是,难道有一天你必须支持几个设备吗?

因此,从概念上讲,即使您目前只有一个设备,唯一性也不是通用设备类的属性,也不是其具体实现。这只是您当前创建DataLink的用例。因此,我建议您实现一个工厂,并派生一个特定于应用程序的工厂来实现您的创造性约束;

class DeviceFactory {  // application independent
public:  
    enum DeviceType { COMDevice, USBDevice, ... };
    DataLink *CreateDevice(std::string devicename, DeviceType t); 
}; 
class MySpecificFactory : public DeviceFactory {  // application specific constraints
    std::map<std::string,DataLink*> objects; 
public: 
    DataLink *CreateDevice(std::string devicename, DeviceType t) {
        if (objects.count(devicename)!=0) {
            // device already exists, either report an error, or 
            // return the previously created object with the same name (provided it has the same type)
            ...
        }
        else {
            DataLink* dl = DeviceFactory::CreateDevice(devicename,t); 
            if (dl) 
                objects[devicename]=dl;
            return dl; 
        }
    }   
};

链接特定功能的处理与创建问题正交。最简单、最安全的方法当然是动态播放:

if (COMDevice* cd=dynamic_cast<COMDevice>(dl))    // nullptr if it isn't a   COMDevice 
    cd->COMFunction(); 
else ...    

如果不知道链接特定函数的用途以及它们在应用程序上下文中的关系,很难就更具体的模式提供建议。

您有几个选择,但我个人的经验来自中间件方法。因此,我将推荐这种方法(即使你不是在写"中间件","想法"仍然有用)

我们有几种不同的"物理连接":军用无线电1、军用无线电2、Wifi、USB到以太网等。每种连接都可以被认为与您的不同连接类型相似。

利用桥接模式。。。

旨在"将抽象与其实现解耦,以便两者可以独立变化"。桥接器使用封装、聚合,并且可以使用继承将职责划分为不同的类。

1) 定义所有连接都将使用的接口。

2) 将每个连接类型的基本原子操作封装到"helper"类中。(打开()、关闭()、读取()、写入(字节[]数据)等)

3) 编写一个桥接类,将通用接口转换为每个连接类型的"helper类"实现。

4) 有一些逻辑来确定在给定时间哪个"连接"应该是"活动的",并将"连接接口"与网桥impl相关联。所使用的连接类型。(或连接列表,如果这是多播发送等)

这样就可以了。你有一个单独的接口,你的应用程序的"其余部分"可以从中写入/读取。并且"impl.details"隐藏在原子操作"helper"类和/或桥接器类中。


示例接口://显然非常简单的示例

interface IConnection{
   byte[] read(int size);
   void write(byte[] data);
   bool open();
   bool close();
}

还有一个实现类:

class usb_wrapper{
   // this is completely made up, but made up methods to show pattern as an example
   // these methods are extreme exaggerations and not 'real' at all
   int open(String connectionName, int id){
       // returns connection_id of new connection
   }
   int close(int connection_id){...} // returns a flag if connection was closed
   bool write128byte(byte[] data) {...} // you can only write 128 byte chunks
   byte[] read128byte(){...} // you can only read 128 byte chunks
}

正如你所看到的,上面的片段有"相似之处",但实际的方法有不同的参数、不同的要求等。

桥梁等级:

class usbConnectionBridge implements IConnection{
   usb_connection conn = new usb_connection();
   // Here is where you have the IConnection methods, inside these methods you 
   // have the logic to 'adapt' from these methods ... to the 'conn' object 
    byte[] read(int size){...}
    void write(byte[] data){...}
    bool open(){...}
    bool close(){...}
   // possibly additional helper methods below, etc.
}

因此,"bridge"类将包装(封装)usb_wrapper,并使其能够与接口交互。从而允许接口(抽象)与其实现(usb_wrapper)解耦,以便两者可以独立变化"这是定义的桥接模式。