gpt4 book ai didi

c - C 中的设计模式 - 从多个设备和接口(interface)读取

转载 作者:行者123 更新时间:2023-12-04 06:02:18 28 4
gpt4 key购买 nike

我需要实现一些功能,从具有不同接口(interface)可能性和不同消息结构的不同设备读取消息。 (但消息具有几乎相同的数据)

例如

Device_A {
message type: A
iface 1: tcp
}

Device_B {
message type: B
iface 1: serial
iface 2: tcp
}
... and so on

在我的主要...
struct msg_data;
while(user_wants_to_read) {
read_msg(); // reads and sets data in msg_data
do_work(msg_data);
}

在 OO 语言中,我会使用策略模式。我想我可以用 void* read_func; 来做到这一点?

我对 C 语言缺乏经验,我想像一个优秀的 C 程序员那样学习编程。我应该实现什么样的设计模式/功能?

最佳答案

听起来您有两个或更多不同的抽象要解决:

  • 不同的流源(TCP 与串行)。设备 A 和设备 B 的 TCP 协议(protocol)是否相同?
  • 结构不同但语义相同的不同消息类型。
  • 不同的设备类别(设备 A 与设备 B)

  • 我将专注于使用工厂从流中读取的策略模式。然后可能是用于将更多数据放入消息对象的适配器或策略模式。但我不会纠结于“哪种设计模式”。更有可能的是,只考虑接口(interface)。

    因此,首先,也许将串行和 TCP 流抽象为具有相同接口(interface)的不同实现。一种知道如何从 TCP 套接字连接和读取字节而不考虑消息内容的实现。另一个知道如何从串行端口读取。它们应该具有相同的“界面”。这是一个“字节流接口(interface)”的轻量级示例,其中抛出了一些黑客攻击的套接字代码。如果这不能编译,请原谅我。我可能有一个在 C++ 中有效的错字,但在 C 中是错误的。无论如何,这只是一个通过函数表指针演示接口(interface)的示例。

    我的想法是,“我将如何在 C++ 中实现它?”然后我将我的答案转换为纯“C”。 (注意:我可能会在下面犯一些声明错误。)
    struct ByteStreamer;

    typedef int (*ReadFunc)(ByteStreamer*, char* buffer, int count);
    typedef int (*OpenFunc)(ByteStreamer*, char* url); // maybe 'open' isn't needed if it's handled by the factory
    typedef int (*CloseFunc)(ByteStreamer*);
    typedef void (*DisposeFunc)(ByteStreamer*);

    typedef struct _ByteStreamer
    {
    ReadFunc readfunc;
    OpenFunc openfunc;
    CloseFunc closefunc;
    DisposeFunc dispose;

    // private data meant for the "class"
    void* instancedata;
    } ByteStreamer;

    struct _tcpconnection
    {
    int socket;
    sockaddr_in addrRemote;
    } TCPConnection;

    struct _serialconnection
    {
    int filehandle;
    int baud;
    } SerialConnection;

    // ---------------------------------------

    ByteStream* CreateStreamForTCP(const sockaddr_in *pAddr) // pass additional parameter as needed
    {
    ByteStreamer* pStream = (ByteStreamre*)malloc(sizeof(ByteStreamer));
    TCPConnection* pTCPConnection = (TCPConnection*)malloc(sizeof(TCPConnection*));
    pTCPConnection->socket = -1;
    pTCPConnection->addrRemote = *pAddr;
    pStream->instancedata = pTCPConnection;
    pStream->ReadFunc = TCPRead;
    pStream->OpenFunc = TCPOpen;
    pStream->CloseFunc = TCPClose;
    pStream->DisposeFunc = TCPDispose;
    pStream->type = STREAM_TYPE_TCP;
    return pStream;
    }

    int TCPRead(ByteStream* pStream, char* buffer, int count)
    {
    return recv(((TCPConnection*)pStream->instancedata)->socket, buffer, count, 0);
    }

    int TCPOpen(ByteStream* pStream, char* url)
    {
    // it's up to you if you want to encapsulate the socket address in url or in the instance data
    TCPConnection* pConn = (TCPConnection*)(pStream->instancedata);
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    connect(&pConn->addrRemote, sizeof(pConn->addrRemote));
    return (pConn->sock >= 0); // true/false return;
    }

    void TCPClose(ByteStream* pStream)
    {
    TCPConnection* pConn = (TCPConnection*)(pStream->instancedata);
    close(pConn->sock);
    }
    void TCPDispose(ByteStream* pStream)
    {
    free(pStream->instancedata);
    free(pStream);
    }

    现在将上面的所有 TCP 代码替换为等效的串行端口实现。实现 ByteStream 结构的“文件流”(或“内存流”)版本也是一个好主意。因为它将在更高级别代码的单元测试中非常有用。

    因此,在您完成所有字节流实现之后,然后转到解析设备特定消息。
    typedef struct _Message_A
    {
    // A specific data fields
    } Message_A;

    struct _Message_B
    {
    // B specific data fields
    } Message_B;

    struct Message
    {
    // commonality between Message_A and Message_B
    };

    typedef (*ReadMessageFromStream)(MessageReader* pReader, Message* pMsg); // pStream is an in-param, pMSg is an out-param.

    typedef (*MessageReaderDispose)();

    struct MessageReader
    {
    ReadMessageFromStream reader;
    MessageReaderDispose dispose;

    // -----------------------------
    ByteStream* pStream;
    void *instancedata;
    };


    // function to read a "Message_A" from a stream - and then transpose it to the generic Message type
    int ReadMessage_A(ByteStream* pStream, Message* pMsg);
    // function to read a "Message_B" from a stream - and then transpose it to the generic Message type
    int ReadMessage_B(ByteStream* pStream, Message* pMsg);

    因此,实现 ReadMessage_A 和 ReadMessage_B 真正酷的地方在于,您可以传递 ByteStream 的“文件流”实现并进行一些非常好的单元测试。因此,当您插入 TCP 或串行版本时,它很有可能正常工作(假设您的 TCP 和串行代码是单独测试的)。

    然后也许每个类都有一个工厂方法来创建 uber ReadMessageFromStream:
    MessageReader* CreateTCPReaderForDeviceA(DeviceA* pA, sockaddr_in* pAddr)
    {
    MessageReader *pMR = (vMessageReader*)malloc(sizeof(MessageReader));
    pMR->pStream = CreateStreamForTCP(pAddr);
    pMR->pStream->Open();
    pMR->reader = ReadMessage_A;
    return pMR;
    }

    MessageReader* CreateSerialReaderForDeviceB(DeviceB* pB, int comport)
    {
    MessageReader *pMR = (vMessageReader*)malloc(sizeof(MessageReader));
    pMR->pStream = CreateStreamForSerial(comport);
    pMR->pStream->Open();
    pMR->reader = ReadMessage_B;
    return pMR;
    }

    然后你的主循环看起来像下面这样:
    if ((type == DEVICE_A) && (source == TCP))
    pReader = CreateTCPReaderForDeviceA(pDevice, &addr)
    else if ((type == DEVICE_B) && (source == SERIAL))
    pReader = CreateSerialReaderForDeviceB(pDeviceB, 1);

    // read the message
    Message msg;
    pReader->reader(pReader, &msg);
    pReader->Dispose(); // free all the data allocated and close connections/files

    哇......我厌倦了输入这一点。希望这可以帮助。

    关于c - C 中的设计模式 - 从多个设备和接口(interface)读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8797391/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com