设备数据链路的设计模式

时间:2015-12-28 21:04:58

标签: c++ design-patterns

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

Properties destProperties = new Properties();
destProperties.setProperty("bootstrap.servers", bootstrapServers);
FlinkKafkaProducer<MyAvroObject> kafkaProducer = new FlinkKafkaProducer<L3Result>("MyKafkaTopic", new MySerializationSchema(), destProperties);

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

2 个答案:

答案 0 :(得分:2)

您有几种选择,但我个人的经验来自中间件方法。所以我会推荐这种方法(即使你不是在写中间件&#39;&#39;这些想法仍然有用)

我们有几种不同的物理连接&#39;:军用无线电1,军用无线电2,Wifi,USB到以太网等。其中每一种都可以被认为与您的不同连接类型类似。

利用Bridge Pattern ......

  

意味着将抽象与其实现分离,以便两者可以独立变化&#34;。该桥使用封装,聚合,并且可以使用继承将职责分成不同的类。

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

2)将每种连接类型的基本原子动作封装到&#39;帮助器中。类。 (open(),close(),read(),write(Byte [] data)等)

3)编写一个桥接类,将通用接口转换为&#39;帮助程序类&#39;每种连接类型的实现。

4)有一些逻辑决定了哪个&#39;连接&#39;应该是活跃的&#39;在给定时间,并关联连接接口&#39;与桥接impl。正在使用的连接类型。 (或连接列表,如果这是多播发送等)

应该这样做。你有一个单独的界面,其余的&#39;你的应用程序可以写/读。和#34; impl。细节&#34;被隐藏在你的原子行动中的帮助&#39;帮助&#39;类和/或桥类。

示例界面://显然非常简单的考试

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
}

正如您可以看到上面的片段有相似之处&#39;但实际方法有不同的参数,不同的要求等。

桥类:

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.
}

所以&#39;桥梁&#39; class将包装(封装)usb_wrapper并使其能够与接口进行交互。从而允许接口(抽象)与其实现(usb_wrapper)的解耦,使得两者可以独立地变化&#34;根据定义,这是桥梁模式。

答案 1 :(得分:2)

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

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

现在创建设备会引发一些问题。您的多态设计宁愿代表参数化factory method,其中参数(配置数据?)将告诉您是否要创建COM,USB或WIFI设备:

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

但是你添加了另一个约束:

  

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

事实上,单例的意图不仅是确保单个实例,还要确保全局访问它。如果您不需要这样的全球访问权限,我强烈建议您不要在此处使用单身人士。

顺便说一句,你的约束提出了其他问题:你有每种类型的一个设备吗?或者你有一个类型的设备?最重要的是,有一天,你不得不支持几种设备吗?

因此,从概念上讲,即使您当前只有一个设备,该unicity也不是您的通用设备类的属性,也不是其具体实现。它只是您当前创建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 ...    

如果不了解链接特定功能的用途以及它们在您的应用环境中的相关性,则很难就更具体的模式提出建议。